From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 04266C44501 for ; Wed, 15 Jul 2026 14:30:22 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender:List-Subscribe:List-Help :List-Post:List-Archive:List-Unsubscribe:List-Id:Content-Transfer-Encoding: MIME-Version:References:In-Reply-To:Message-ID:Date:Subject:Cc:To:From: Reply-To:Content-Type:Content-ID:Content-Description:Resent-Date:Resent-From: Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Owner; bh=kPIJOV4AXxs+BeKHV7Wk7hs35/2hWy1ZXmFfXRyjjJU=; b=vfvcwVZIegyNpY75zRlc0f92kG niNebnrblmD2Z5nicMFdImXL0yScpmqu8IKXg6b2CFix8jHu0ZI+BWrVlPDY636djUW1jl22Er3v7 LkNgfM3iY/z7+cakploW6EO+dCgURgdzf+b6ngUFZIaLOoNE6lIap+1+uD+D43+U9wJkuBEajw5Bq 9GfrNtZ8/kvHlz72+L3YYyUnaNMvFDjrAgTjinjpVXNn/BSABh6zsJsrvuOimniKevZIFWFeK6b7T EI92iD2Ly3HfQRAfjPqfXByPu/IQU130TlAcfsziZaAJHFLZ5ey6Puqwt1uxtxCjFFwxpG0EIZYzj uliStWkw==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.99.1 #2 (Red Hat Linux)) id 1wk0co-0000000F8ni-0qGa; Wed, 15 Jul 2026 14:30:14 +0000 Received: from desiato.infradead.org ([2001:8b0:10b:1:d65d:64ff:fe57:4e05]) by bombadil.infradead.org with esmtps (Exim 4.99.1 #2 (Red Hat Linux)) id 1wk0cn-0000000F8m6-0OFH for linux-arm-kernel@bombadil.infradead.org; Wed, 15 Jul 2026 14:30:13 +0000 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=infradead.org; s=desiato.20200630; h=Content-Transfer-Encoding:MIME-Version :References:In-Reply-To:Message-ID:Date:Subject:Cc:To:From:Sender:Reply-To: Content-Type:Content-ID:Content-Description; bh=kPIJOV4AXxs+BeKHV7Wk7hs35/2hWy1ZXmFfXRyjjJU=; b=JMarpiO0pjgVcw9qWgR6NQZ0OL FZPe42+twPKOyO7ROon5d8NO53yi//RXyr1zUs2GGx04BdFguk4IOIVbRIlGIMs/kdaHzTyAuOrjZ 2jnud8opYq4wlBwd+Rcy70He5+KS3kdcW1HXQUAaBgOYcJTM5+HllOtS+9iFXGg7s4i/XcjzBnhVj Neq1iiLz5notAhGaxzljnrcYqvGQ34CaPYQ29PXATmN5pNWZ59VySgwnqTlzwC96U0ouGoo7MDyzz zK7kKoR/Q69KjBveUvygapLazQhmKg5AnkGOZASGoz/eQVE2N3LOEE1f2ouartAOs/Sltyciz616z KZ2mLaIQ==; Received: from foss.arm.com ([217.140.110.172]) by desiato.infradead.org with esmtp (Exim 4.99.2 #2 (Red Hat Linux)) id 1wk0cj-0000000DMuq-3CYY for linux-arm-kernel@lists.infradead.org; Wed, 15 Jul 2026 14:30:11 +0000 Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 3C7F21595; Wed, 15 Jul 2026 07:30:03 -0700 (PDT) Received: from e122027.cambridge.arm.com (e122027.cambridge.arm.com [10.1.25.27]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id C972E3F7B4; Wed, 15 Jul 2026 07:30:02 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1784125807; bh=VER/7iDZloZKoBITKTwf0bwc2DOHOeZOB2ubtxmEj08=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=e845u5qJ2t+B23Z8lI+AgTtMWwXnGL0glXcvMH00NblDc/I9+Rc/Qd9uMQK0LDQGI 9wA3Jh2MK8VZU+DPOm20Iia2TsWvDWN0AsWOnixCgRIJgENJotdaHD3mVWWVtsYbXB qy1W8WI5lWmbPBs2HkL7n+yjNhFqHUXKIBM868gY= From: Steven Price To: kvm@vger.kernel.org, kvmarm@lists.linux.dev Cc: Steven Price , Catalin Marinas , Marc Zyngier , Will Deacon , James Morse , Oliver Upton , Suzuki K Poulose , Zenghui Yu , linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, Joey Gouly , Alexandru Elisei , Christoffer Dall , Fuad Tabba , linux-coco@lists.linux.dev, Ganapatrao Kulkarni , Gavin Shan , Shanker Donthineni , Alper Gun , "Aneesh Kumar K . V" , Emi Kisanuki , Vishal Annapurve , WeiLin.Chang@arm.com, Lorenzo Pieralisi Subject: [PATCH v15 15/37] KVM: arm64: CCA: Handle RMI_EXIT_RIPAS_CHANGE Date: Wed, 15 Jul 2026 15:28:17 +0100 Message-ID: <20260715142841.80544-16-steven.price@arm.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260715142841.80544-1-steven.price@arm.com> References: <20260715142841.80544-1-steven.price@arm.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.9.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20260715_153010_238398_DE766A0B X-CRM114-Status: GOOD ( 33.24 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+linux-arm-kernel=archiver.kernel.org@lists.infradead.org The guest can request that a region of its protected address space is switched between RIPAS_RAM and RIPAS_EMPTY (and back) using RSI_IPA_STATE_SET. This causes a guest exit with the RMI_EXIT_RIPAS_CHANGE code. We treat this as a request to convert a protected region to unprotected (or back), exiting to the VMM to make the necessary changes to the guest_memfd and memslot mappings. On the next entry the RIPAS changes are committed by making RMI_RTT_SET_RIPAS calls. The VMM may wish to reject the RIPAS change requested by the guest. For now it can only do this by no longer scheduling the VCPU as we don't currently have a usecase for returning that rejection to the guest, but by postponing the RMI_RTT_SET_RIPAS changes to entry we leave the door open for adding a new ioctl in the future for this purpose. Signed-off-by: Steven Price --- Changes since v14: * Use addition rather than bitwise OR for adding the shared_bit in realm_unmap_shared_range(), this handles the case where the region includes the last address (which means 'end' already has the bit set). Changes since v13: * Switch to the new RMI_RTT_UNPROT_UNMAP range-based API. * Drop ugly hack for RMM bug which errored when the RIPAS was already set to the desired value. Changes since v12: * Switch to the new RMM v2.0 RMI_RTT_DATA_UNMAP which can unmap an address range. Changes since v11: * Combine the "Allow VMM to set RIPAS" patch into this one to avoid adding functions before they are used. * Drop the CAP for setting RIPAS and adapt to changes from previous patches. Changes since v10: * Add comment explaining the assignment of rec->run->exit.ripas_base in kvm_complete_ripas_change(). Changes since v8: * Make use of ripas_change() from a previous patch to implement realm_set_ipa_state(). * Update exit.ripas_base after a RIPAS change so that, if instead of entering the guest we exit to user space, we don't attempt to repeat the RIPAS change (triggering an error from the RMM). Changes since v7: * Rework the loop in realm_set_ipa_state() to make it clear when the 'next' output value of rmi_rtt_set_ripas() is used. New patch for v7: The code was previously split awkwardly between two other patches. --- arch/arm64/include/asm/kvm_rmi.h | 6 + arch/arm64/kvm/mmu.c | 8 +- arch/arm64/kvm/rmi.c | 457 +++++++++++++++++++++++++++++++ 3 files changed, 468 insertions(+), 3 deletions(-) diff --git a/arch/arm64/include/asm/kvm_rmi.h b/arch/arm64/include/asm/kvm_rmi.h index b1e4cf0f6803..5461c49bea4d 100644 --- a/arch/arm64/include/asm/kvm_rmi.h +++ b/arch/arm64/include/asm/kvm_rmi.h @@ -104,6 +104,12 @@ int kvm_rec_enter(struct kvm_vcpu *vcpu); int kvm_rec_pre_enter(struct kvm_vcpu *vcpu); int handle_rec_exit(struct kvm_vcpu *vcpu, int rec_run_status); +void kvm_realm_unmap_range(struct kvm *kvm, + unsigned long ipa, + unsigned long size, + bool unmap_private, + bool may_block); + 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 cd06881c1497..dcc2ab08d0e4 100644 --- a/arch/arm64/kvm/mmu.c +++ b/arch/arm64/kvm/mmu.c @@ -319,6 +319,7 @@ static void invalidate_icache_guest_page(void *va, size_t size) * @start: The intermediate physical base address of the range to unmap * @size: The size of the area to unmap * @may_block: Whether or not we are permitted to block + * @only_shared: If true then protected mappings should not be unmapped * * Clear a range of stage-2 mappings, lowering the various ref-counts. Must * be called while holding mmu_lock (unless for freeing the stage2 pgd before @@ -326,7 +327,7 @@ static void invalidate_icache_guest_page(void *va, size_t size) * with things behind our backs. */ static void __unmap_stage2_range(struct kvm_s2_mmu *mmu, phys_addr_t start, u64 size, - bool may_block) + bool may_block, bool only_shared) { struct kvm *kvm = kvm_s2_mmu_to_kvm(mmu); phys_addr_t end = start + size; @@ -343,7 +344,7 @@ void kvm_stage2_unmap_range(struct kvm_s2_mmu *mmu, phys_addr_t start, if (kvm_vm_is_protected(kvm_s2_mmu_to_kvm(mmu))) return; - __unmap_stage2_range(mmu, start, size, may_block); + __unmap_stage2_range(mmu, start, size, may_block, false); } void kvm_stage2_flush_range(struct kvm_s2_mmu *mmu, phys_addr_t addr, phys_addr_t end) @@ -2467,7 +2468,8 @@ bool kvm_unmap_gfn_range(struct kvm *kvm, struct kvm_gfn_range *range) __unmap_stage2_range(&kvm->arch.mmu, range->start << PAGE_SHIFT, (range->end - range->start) << PAGE_SHIFT, - range->may_block); + range->may_block, + !(range->attr_filter & KVM_FILTER_PRIVATE)); kvm_nested_s2_unmap(kvm, range->may_block); return false; diff --git a/arch/arm64/kvm/rmi.c b/arch/arm64/kvm/rmi.c index 430b82c02d15..1ef676eed172 100644 --- a/arch/arm64/kvm/rmi.c +++ b/arch/arm64/kvm/rmi.c @@ -34,6 +34,83 @@ static int get_start_level(struct realm *realm) return 4 - stage2_pgtable_levels(realm->ia_bits); } +static int find_map_level(struct realm *realm, + unsigned long start, + unsigned long end) +{ + int level = KVM_PGTABLE_LAST_LEVEL; + + while (level > get_start_level(realm)) { + unsigned long map_size = rmi_rtt_level_mapsize(level - 1); + + if (!IS_ALIGNED(start, map_size) || + (start + map_size) > end) + break; + + level--; + } + + return level; +} + +static unsigned long rmi_range_entry_size(int rmi_size) +{ + if (WARN_ON(rmi_size > KVM_PGTABLE_LAST_LEVEL)) + return 0; + + return kvm_granule_size(KVM_PGTABLE_LAST_LEVEL - rmi_size); +} + +static int undelegate_range_desc(unsigned long desc) +{ + unsigned long size = rmi_range_entry_size(RMI_ADDR_RANGE_SIZE(desc)); + unsigned long count = RMI_ADDR_RANGE_COUNT(desc); + unsigned long addr = RMI_ADDR_RANGE_ADDR(desc); + unsigned long state = RMI_ADDR_RANGE_STATE(desc); + + if (state == RMI_OP_MEM_UNDELEGATED) + return 0; + + if (size * count == 0) + return 0; + + return rmi_undelegate_range(addr, size * count); +} + +static phys_addr_t alloc_delegated_granule(struct kvm_mmu_memory_cache *mc) +{ + phys_addr_t phys; + void *virt; + + if (mc) { + virt = kvm_mmu_memory_cache_alloc(mc); + } else { + virt = (void *)__get_free_page(GFP_ATOMIC | __GFP_ZERO | + __GFP_ACCOUNT); + } + + if (!virt) + return PHYS_ADDR_MAX; + + phys = virt_to_phys(virt); + if (rmi_delegate_page(phys)) { + free_page((unsigned long)virt); + return PHYS_ADDR_MAX; + } + + return phys; +} + +static phys_addr_t alloc_rtt(struct kvm_mmu_memory_cache *mc) +{ + phys_addr_t phys = alloc_delegated_granule(mc); + + if (phys != PHYS_ADDR_MAX) + kvm_account_pgtable_pages(phys_to_virt(phys), 1); + + return phys; +} + static void free_rtt(phys_addr_t phys) { if (free_delegated_page(phys)) @@ -42,6 +119,32 @@ static void free_rtt(phys_addr_t phys) kvm_account_pgtable_pages(phys_to_virt(phys), -1); } +static int realm_rtt_create(struct realm *realm, + unsigned long addr, + int level, + phys_addr_t phys) +{ + addr = ALIGN_DOWN(addr, rmi_rtt_level_mapsize(level - 1)); + return rmi_rtt_create(virt_to_phys(realm->rd), phys, addr, level); +} + +static int realm_rtt_fold(struct realm *realm, + unsigned long addr, + int level, + phys_addr_t *rtt_granule) +{ + unsigned long out_rtt; + int ret; + + addr = ALIGN_DOWN(addr, rmi_rtt_level_mapsize(level - 1)); + ret = rmi_rtt_fold(virt_to_phys(realm->rd), addr, level, &out_rtt); + + if (rtt_granule) + *rtt_granule = out_rtt; + + return ret; +} + /* * realm_rtt_destroy - Destroy an RTT at @level for @addr. * @@ -65,6 +168,38 @@ static int realm_rtt_destroy(struct realm *realm, unsigned long addr, return ret; } +static int realm_create_rtt_levels(struct realm *realm, + unsigned long ipa, + int level, + int max_level, + struct kvm_mmu_memory_cache *mc) +{ + while (level++ < max_level) { + phys_addr_t rtt = alloc_rtt(mc); + int ret; + + if (rtt == PHYS_ADDR_MAX) + return -ENOMEM; + + ret = realm_rtt_create(realm, ipa, level, rtt); + if (RMI_RETURN_STATUS(ret) == RMI_ERROR_RTT && + RMI_RETURN_INDEX(ret) == level - 1) { + /* The RTT already exists, continue */ + free_rtt(rtt); + continue; + } + + if (ret) { + WARN(1, "Failed to create RTT at level %d: %d\n", + level, ret); + free_rtt(rtt); + return -ENXIO; + } + } + + return 0; +} + static int realm_tear_down_rtt_level(struct realm *realm, int level, unsigned long start, unsigned long end) { @@ -159,6 +294,62 @@ static int realm_tear_down_rtt_range(struct realm *realm, start, end); } +/* + * Returns 0 on successful fold, a negative value on error, a positive value if + * we were not able to fold all tables at this level. + */ +static int realm_fold_rtt_level(struct realm *realm, int level, + unsigned long start, unsigned long end) +{ + int not_folded = 0; + ssize_t map_size; + unsigned long addr, next_addr; + + if (WARN_ON(level > KVM_PGTABLE_LAST_LEVEL)) + return -EINVAL; + + map_size = rmi_rtt_level_mapsize(level - 1); + + for (addr = start; addr < end; addr = next_addr) { + phys_addr_t rtt_granule; + int ret; + unsigned long align_addr = ALIGN(addr, map_size); + + next_addr = ALIGN(addr + 1, map_size); + + ret = realm_rtt_fold(realm, align_addr, level, &rtt_granule); + + switch (RMI_RETURN_STATUS(ret)) { + case RMI_SUCCESS: + free_rtt(rtt_granule); + break; + case RMI_ERROR_RTT: + if (level == KVM_PGTABLE_LAST_LEVEL || + RMI_RETURN_INDEX(ret) < level) { + not_folded++; + break; + } + /* Recurse a level deeper */ + ret = realm_fold_rtt_level(realm, + level + 1, + addr, + next_addr); + if (ret < 0) { + return ret; + } else if (ret == 0) { + /* Try again at this level */ + next_addr = addr; + } + break; + default: + WARN_ON(1); + return -ENXIO; + } + } + + return not_folded; +} + static int realm_destroy_rtts(struct kvm *kvm) { struct realm *realm = &kvm->arch.realm; @@ -203,6 +394,269 @@ int kvm_realm_teardown_stage2(struct kvm *kvm) return realm_destroy_rtts(kvm); } +static void realm_unmap_shared_range(struct kvm *kvm, + unsigned long start, + unsigned long end, + bool may_block) +{ + struct realm *realm = &kvm->arch.realm; + unsigned long rd = virt_to_phys(realm->rd); + unsigned long next_addr, addr; + unsigned long shared_bit = BIT(realm->ia_bits - 1); + + if (start >= end) + return; + + start += shared_bit; + end += shared_bit; + + for (addr = start; addr < end; addr = next_addr) { + int ret; + + ret = rmi_rtt_unprot_unmap(rd, addr, end, RMI_ADDR_TYPE_NONE, + 0, &next_addr, NULL, NULL); + switch (RMI_RETURN_STATUS(ret)) { + case RMI_SUCCESS: + break; + case RMI_ERROR_RTT: { + int err_level = RMI_RETURN_INDEX(ret); + int level = find_map_level(realm, addr, end); + + if (err_level >= level) { + /* Nothing present, so skip */ + next_addr = addr + rmi_rtt_level_mapsize(err_level); + break; + } + + ret = realm_create_rtt_levels(realm, addr, err_level, + level, NULL); + if (WARN_ON(ret)) + return; + /* Retry with the RTT levels in place */ + next_addr = addr; + break; + } + default: + WARN_ON(1); + return; + } + + if (may_block) + cond_resched_rwlock_write(&kvm->mmu_lock); + } + + realm_fold_rtt_level(realm, get_start_level(realm) + 1, + start, end); +} + +static void realm_unmap_private_range(struct kvm *kvm, + unsigned long start, + unsigned long end, + bool may_block) +{ + struct realm *realm = &kvm->arch.realm; + unsigned long rd = virt_to_phys(realm->rd); + unsigned long next_addr, addr; + int ret; + + for (addr = start; addr < end; addr = next_addr) { + unsigned long out_range; + unsigned long flags = RMI_ADDR_TYPE_SINGLE; + /* TODO: Optimise using RMI_ADDR_TYPE_LIST */ + +retry: + ret = rmi_rtt_data_unmap(rd, addr, end, flags, 0, + &next_addr, &out_range, NULL); + + if (RMI_RETURN_STATUS(ret) == RMI_ERROR_RTT) { + phys_addr_t rtt; + + if (next_addr > addr) + continue; /* UNASSIGNED */ + + rtt = alloc_rtt(NULL); + if (WARN_ON(rtt == PHYS_ADDR_MAX)) + return; + ret = realm_rtt_create(realm, addr, + RMI_RETURN_INDEX(ret) + 1, rtt); + if (WARN_ON(ret)) { + free_rtt(rtt); + return; + } + goto retry; + } else if (WARN_ON(ret)) { + continue; + } + + ret = undelegate_range_desc(out_range); + if (WARN_ON(ret)) + break; + + if (may_block) + cond_resched_rwlock_write(&kvm->mmu_lock); + } + + realm_fold_rtt_level(realm, get_start_level(realm) + 1, + start, end); +} + +void kvm_realm_unmap_range(struct kvm *kvm, unsigned long start, + unsigned long size, bool unmap_private, + bool may_block) +{ + unsigned long end = start + size; + struct realm *realm = &kvm->arch.realm; + + if (!kvm_realm_is_created(kvm)) + return; + + end = min(BIT(realm->ia_bits - 1), end); + + realm_unmap_shared_range(kvm, start, end, may_block); + if (unmap_private) + realm_unmap_private_range(kvm, start, end, may_block); +} + +enum ripas_action { + RIPAS_INIT, + RIPAS_SET, +}; + +static int ripas_change(struct kvm *kvm, + struct kvm_vcpu *vcpu, + unsigned long ipa, + unsigned long end, + enum ripas_action action, + unsigned long *top_ipa) +{ + struct realm *realm = &kvm->arch.realm; + phys_addr_t rd_phys = virt_to_phys(realm->rd); + phys_addr_t rec_phys; + struct kvm_mmu_memory_cache *memcache = NULL; + int ret = 0; + + if (vcpu) { + rec_phys = virt_to_phys(vcpu->arch.rec.rec_page); + memcache = &vcpu->arch.mmu_page_cache; + + WARN_ON(action != RIPAS_SET); + } else { + WARN_ON(action != RIPAS_INIT); + } + + while (ipa < end) { + unsigned long next = ~0; + + switch (action) { + case RIPAS_INIT: + ret = rmi_rtt_init_ripas(rd_phys, ipa, end, &next); + break; + case RIPAS_SET: + ret = rmi_rtt_set_ripas(rd_phys, rec_phys, ipa, end, + &next); + break; + } + + switch (RMI_RETURN_STATUS(ret)) { + case RMI_SUCCESS: + ipa = next; + break; + case RMI_ERROR_RTT: { + int err_level = RMI_RETURN_INDEX(ret); + int level = find_map_level(realm, ipa, end); + + /* + * If the operation failed at deeper level than + * what is required for the address range, this + * implies encountering an unexpected entry, + * (e.g., RIPAS_DESTROYED), which the RMM prevents + * us from modifying. This is only applicable for + * RMI_RTT_INIT_RIPAS. All the other requests + * are generated by the Realm and thus RMM should + * be able to allow the transition. + */ + if (action == RIPAS_INIT && WARN_ON_ONCE(err_level >= level)) + return -ENXIO; + + ret = realm_create_rtt_levels(realm, ipa, err_level, + level, memcache); + if (ret) + goto out; + /* Retry with the RTT levels in place */ + break; + } + default: + WARN_ON(1); + ret = -ENXIO; + goto out; + } + } + +out: + if (top_ipa) + *top_ipa = ipa; + + return ret; +} + +static int realm_set_ipa_state(struct kvm_vcpu *vcpu, + unsigned long start, + unsigned long end, + unsigned long ripas, + unsigned long *top_ipa) +{ + struct kvm *kvm = vcpu->kvm; + int ret = ripas_change(kvm, vcpu, start, end, RIPAS_SET, top_ipa); + + if (!ret && ripas == RMI_EMPTY && *top_ipa != start) + realm_unmap_private_range(kvm, start, *top_ipa, false); + + return ret; +} + +static void kvm_complete_ripas_change(struct kvm_vcpu *vcpu) +{ + struct kvm *kvm = vcpu->kvm; + struct realm_rec *rec = &vcpu->arch.rec; + unsigned long base = rec->run->exit.ripas_base; + unsigned long top = rec->run->exit.ripas_top; + unsigned long ripas = rec->run->exit.ripas_value; + unsigned long top_ipa = base; + int ret; + + do { + kvm_mmu_topup_memory_cache(&vcpu->arch.mmu_page_cache, + kvm_mmu_cache_min_pages(vcpu->arch.hw_mmu)); + write_lock(&kvm->mmu_lock); + ret = realm_set_ipa_state(vcpu, base, top, ripas, &top_ipa); + write_unlock(&kvm->mmu_lock); + + if (ret == -ENOMEM) { + /* If no progress, then stop */ + if (top_ipa == base) + break; + base = top_ipa; + continue; + } + + if (WARN_RATELIMIT(ret, + "Unable to satisfy RIPAS_CHANGE for %#lx - %#lx, ripas: %#lx\n", + base, top, ripas)) + break; + + base = top_ipa; + } while (base < top); + + /* + * If this function is called again before the REC_ENTER call then + * avoid calling realm_set_ipa_state() again by changing to the value + * of ripas_base for the part that has already been covered. The RMM + * ignores the contains of the rec_exit structure so this doesn't + * affect the RMM. + */ + rec->run->exit.ripas_base = base; +} + /* * kvm_rec_pre_enter - Complete operations before entering a REC * @@ -227,6 +681,9 @@ int kvm_rec_pre_enter(struct kvm_vcpu *vcpu) for (int i = 0; i < REC_RUN_GPRS; i++) rec->run->enter.gprs[i] = vcpu_get_reg(vcpu, i); break; + case RMI_EXIT_RIPAS_CHANGE: + kvm_complete_ripas_change(vcpu); + break; } return 1; -- 2.43.0