From 6909d18e99f16f9778e7a39e3d7e6c0e98cd365f Mon Sep 17 00:00:00 2001 From: Sebastian Ene Date: Tue, 14 Feb 2023 12:06:42 +0000 Subject: [PATCH] ANDROID: KVM: arm64: Support pVM memory sharing with the hypervisor Allow a protected guest VM to share memory pages with the hypervisor. Return the hypervisor virtual address of the page being shared to the caller to prevent additional walks of the guest s2 pagetable. Bug: 269285339 Bug: 278749606 Change-Id: I18a43667f94debf660b88799bda276773144855a Signed-off-by: Sebastian Ene --- arch/arm64/kvm/hyp/include/nvhe/mem_protect.h | 2 + arch/arm64/kvm/hyp/nvhe/mem_protect.c | 78 +++++++++++++++++++ 2 files changed, 80 insertions(+) diff --git a/arch/arm64/kvm/hyp/include/nvhe/mem_protect.h b/arch/arm64/kvm/hyp/include/nvhe/mem_protect.h index d496ff3b8d86..c37af4366d78 100644 --- a/arch/arm64/kvm/hyp/include/nvhe/mem_protect.h +++ b/arch/arm64/kvm/hyp/include/nvhe/mem_protect.h @@ -45,6 +45,8 @@ int ___pkvm_host_donate_hyp_prot(u64 pfn, u64 nr_pages, bool accept_mmio, enum kvm_pgtable_prot prot); int __pkvm_host_donate_hyp_locked(u64 pfn, u64 nr_pages, enum kvm_pgtable_prot prot); int __pkvm_hyp_donate_host(u64 pfn, u64 nr_pages); +int __pkvm_guest_share_hyp_page(struct pkvm_hyp_vcpu *vcpu, u64 ipa, u64 *hyp_va); +int __pkvm_guest_unshare_hyp_page(struct pkvm_hyp_vcpu *vcpu, u64 ipa); int __pkvm_host_share_ffa(u64 pfn, u64 nr_pages); int __pkvm_host_unshare_ffa(u64 pfn, u64 nr_pages); int __pkvm_host_donate_guest(u64 pfn, u64 gfn, struct pkvm_hyp_vcpu *vcpu, u64 nr_pages); diff --git a/arch/arm64/kvm/hyp/nvhe/mem_protect.c b/arch/arm64/kvm/hyp/nvhe/mem_protect.c index c4c3e24a9f2c..64cdaa64b09b 100644 --- a/arch/arm64/kvm/hyp/nvhe/mem_protect.c +++ b/arch/arm64/kvm/hyp/nvhe/mem_protect.c @@ -1341,6 +1341,84 @@ unlock: return ret; } +int __pkvm_guest_share_hyp_page(struct pkvm_hyp_vcpu *vcpu, u64 ipa, u64 *hyp_va) +{ + int ret; + struct pkvm_hyp_vm *vm = pkvm_hyp_vcpu_to_hyp_vm(vcpu); + kvm_pte_t pte; + u64 phys; + enum kvm_pgtable_prot prot; + void *virt; + u64 nr_pages = 1; + + hyp_lock_component(); + guest_lock_component(vm); + + ret = __guest_request_page_transition(ipa, &pte, &nr_pages, vcpu, PKVM_PAGE_OWNED); + if (ret) + goto unlock; + + phys = kvm_pte_to_phys(pte); + + virt = __hyp_va(phys); + if (IS_ENABLED(CONFIG_NVHE_EL2_DEBUG)) { + ret = __hyp_check_page_state_range((u64)virt, PAGE_SIZE, PKVM_NOPAGE); + if (ret) + goto unlock; + } + + prot = pkvm_mkstate(PAGE_HYP, PKVM_PAGE_SHARED_BORROWED); + ret = pkvm_create_mappings_locked(virt, virt + PAGE_SIZE, prot); + if (ret) { + /* + * Repaint the return code as we need to distinguish between the + * no memory from the guest which is recoverable and no memory + * from the hypervisor. + */ + if (ret == -ENOMEM) + ret = -EBUSY; + goto unlock; + } + + WARN_ON(__guest_initiate_page_transition(ipa, pte, nr_pages, vcpu, PKVM_PAGE_SHARED_OWNED)); + *hyp_va = (u64)virt; +unlock: + guest_unlock_component(vm); + hyp_unlock_component(); + + return ret; +} + +int __pkvm_guest_unshare_hyp_page(struct pkvm_hyp_vcpu *vcpu, u64 ipa) +{ + int ret; + struct pkvm_hyp_vm *vm = pkvm_hyp_vcpu_to_hyp_vm(vcpu); + kvm_pte_t pte; + u64 phys, virt, nr_pages = 1; + + hyp_lock_component(); + guest_lock_component(vm); + + ret = __guest_request_page_transition(ipa, &pte, &nr_pages, vcpu, PKVM_PAGE_SHARED_OWNED); + if (ret) + goto unlock; + + phys = kvm_pte_to_phys(pte); + + virt = (u64)__hyp_va(phys); + ret = __hyp_check_page_state_range(virt, PAGE_SIZE, PKVM_PAGE_SHARED_BORROWED); + if (ret) + goto unlock; + + WARN_ON(kvm_pgtable_hyp_unmap(&pkvm_pgtable, virt, PAGE_SIZE) != PAGE_SIZE); + WARN_ON(__guest_initiate_page_transition(ipa, pte, nr_pages, vcpu, PKVM_PAGE_OWNED)); +unlock: + guest_unlock_component(vm); + hyp_unlock_component(); + + return ret; +} + int __pkvm_guest_unshare_host(struct pkvm_hyp_vcpu *vcpu, u64 ipa, u64 nr_pages, u64 *nr_unshared) {