From cea326332b17b4166058b4086d095c4daaf71fac Mon Sep 17 00:00:00 2001 From: Vincent Donnefort Date: Thu, 29 Feb 2024 18:08:06 +0000 Subject: [PATCH] ANDROID: KVM: arm64: Add a range to the guest share/unshare hypercalls Extend the guest share and unshare HVCs with a size argument. For ABI compabitiliy with guests, a 0 range is treated as a single page, while the range argument is advertised in the meminfo HVC. Bug: 357781595 Bug: 243642516 Change-Id: Ic4927124282c430cf57f112eb96ea01740ff9967 Signed-off-by: Vincent Donnefort --- arch/arm64/kvm/hyp/include/nvhe/mem_protect.h | 6 +- arch/arm64/kvm/hyp/nvhe/hyp-main.c | 4 - arch/arm64/kvm/hyp/nvhe/mem_protect.c | 12 ++- arch/arm64/kvm/hyp/nvhe/pkvm.c | 81 ++++++++++--------- arch/arm64/kvm/hypercalls.c | 10 --- include/linux/arm-smccc.h | 2 + 6 files changed, 57 insertions(+), 58 deletions(-) diff --git a/arch/arm64/kvm/hyp/include/nvhe/mem_protect.h b/arch/arm64/kvm/hyp/include/nvhe/mem_protect.h index 3838ce532f56..64446d00bdf1 100644 --- a/arch/arm64/kvm/hyp/include/nvhe/mem_protect.h +++ b/arch/arm64/kvm/hyp/include/nvhe/mem_protect.h @@ -53,8 +53,10 @@ int __pkvm_host_unshare_guest(u64 gfn, struct pkvm_hyp_vm *hyp_vm); int __pkvm_host_donate_guest(u64 pfn, u64 gfn, struct pkvm_hyp_vcpu *vcpu); int __pkvm_host_relax_guest_perms(u64 gfn, enum kvm_pgtable_prot prot, struct pkvm_hyp_vcpu *vcpu); int __pkvm_host_wrprotect_guest(u64 gfn, struct pkvm_hyp_vm *hyp_vm); -int __pkvm_guest_share_host(struct pkvm_hyp_vcpu *hyp_vcpu, u64 ipa); -int __pkvm_guest_unshare_host(struct pkvm_hyp_vcpu *hyp_vcpu, u64 ipa); +int __pkvm_guest_share_host(struct pkvm_hyp_vcpu *hyp_vcpu, u64 ipa, + u64 nr_pages, u64 *nr_shared); +int __pkvm_guest_unshare_host(struct pkvm_hyp_vcpu *hyp_vcpu, u64 ipa, + u64 nr_pages, u64 *nr_unshared); int __pkvm_install_ioguard_page(struct pkvm_hyp_vcpu *hyp_vcpu, u64 ipa); int __pkvm_remove_ioguard_page(struct pkvm_hyp_vcpu *hyp_vcpu, u64 ipa); bool __pkvm_check_ioguard_page(struct pkvm_hyp_vcpu *hyp_vcpu); diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-main.c b/arch/arm64/kvm/hyp/nvhe/hyp-main.c index b6d73d688abb..91367ab7cef3 100644 --- a/arch/arm64/kvm/hyp/nvhe/hyp-main.c +++ b/arch/arm64/kvm/hyp/nvhe/hyp-main.c @@ -374,10 +374,6 @@ static void handle_pvm_exit_hvc64(struct pkvm_hyp_vcpu *hyp_vcpu) n = 1; break; - case ARM_SMCCC_VENDOR_HYP_KVM_MEM_SHARE_FUNC_ID: - fallthrough; - case ARM_SMCCC_VENDOR_HYP_KVM_MEM_UNSHARE_FUNC_ID: - fallthrough; case ARM_SMCCC_VENDOR_HYP_KVM_MEM_RELINQUISH_FUNC_ID: n = 4; break; diff --git a/arch/arm64/kvm/hyp/nvhe/mem_protect.c b/arch/arm64/kvm/hyp/nvhe/mem_protect.c index 4ec8cb70f5f4..f7f8b75e136b 100644 --- a/arch/arm64/kvm/hyp/nvhe/mem_protect.c +++ b/arch/arm64/kvm/hyp/nvhe/mem_protect.c @@ -1222,12 +1222,13 @@ unlock: return ret; } -int __pkvm_guest_share_host(struct pkvm_hyp_vcpu *vcpu, u64 ipa) +int __pkvm_guest_share_host(struct pkvm_hyp_vcpu *vcpu, u64 ipa, u64 nr_pages, + u64 *nr_shared) { struct pkvm_hyp_vm *vm = pkvm_hyp_vcpu_to_hyp_vm(vcpu); - u64 phys, nr_pages = 1; kvm_pte_t pte; size_t size; + u64 phys; int ret; host_lock_component(); @@ -1246,6 +1247,7 @@ int __pkvm_guest_share_host(struct pkvm_hyp_vcpu *vcpu, u64 ipa) WARN_ON(__guest_initiate_page_transition(ipa, pte, nr_pages, vcpu, PKVM_PAGE_SHARED_OWNED)); WARN_ON(__host_set_page_state_range(phys, size, PKVM_PAGE_SHARED_BORROWED)); psci_mem_protect_dec(nr_pages); + *nr_shared = nr_pages; unlock: guest_unlock_component(vm); @@ -1254,12 +1256,13 @@ unlock: return ret; } -int __pkvm_guest_unshare_host(struct pkvm_hyp_vcpu *vcpu, u64 ipa) +int __pkvm_guest_unshare_host(struct pkvm_hyp_vcpu *vcpu, u64 ipa, u64 nr_pages, + u64 *nr_unshared) { struct pkvm_hyp_vm *vm = pkvm_hyp_vcpu_to_hyp_vm(vcpu); - u64 phys, nr_pages = 1; kvm_pte_t pte; size_t size; + u64 phys; int ret; host_lock_component(); @@ -1278,6 +1281,7 @@ int __pkvm_guest_unshare_host(struct pkvm_hyp_vcpu *vcpu, u64 ipa) WARN_ON(__guest_initiate_page_transition(ipa, pte, nr_pages, vcpu, PKVM_PAGE_OWNED)); psci_mem_protect_inc(nr_pages); WARN_ON(host_stage2_set_owner_locked(phys, size, PKVM_ID_GUEST)); + *nr_unshared = nr_pages; unlock: guest_unlock_component(vm); diff --git a/arch/arm64/kvm/hyp/nvhe/pkvm.c b/arch/arm64/kvm/hyp/nvhe/pkvm.c index 68c5c98a89d4..a4a0a645c616 100644 --- a/arch/arm64/kvm/hyp/nvhe/pkvm.c +++ b/arch/arm64/kvm/hyp/nvhe/pkvm.c @@ -1342,30 +1342,6 @@ static bool pkvm_handle_psci(struct pkvm_hyp_vcpu *hyp_vcpu) return pvm_psci_not_supported(hyp_vcpu); } -static u64 __pkvm_memshare_page_req(struct pkvm_hyp_vcpu *hyp_vcpu, u64 ipa) -{ - struct kvm_vcpu *vcpu = &hyp_vcpu->vcpu; - u64 elr; - - /* Fake up a data abort (Level 3 translation fault on write) */ - vcpu->arch.fault.esr_el2 = (u32)ESR_ELx_EC_DABT_LOW << ESR_ELx_EC_SHIFT | - ESR_ELx_WNR | ESR_ELx_FSC_FAULT | - FIELD_PREP(ESR_ELx_FSC_LEVEL, 3); - - /* Shuffle the IPA around into the HPFAR */ - vcpu->arch.fault.hpfar_el2 = (ipa >> 8) & HPFAR_MASK; - - /* This is a virtual address. 0's good. Let's go with 0. */ - vcpu->arch.fault.far_el2 = 0; - - /* Rewind the ELR so we return to the HVC once the IPA is mapped */ - elr = read_sysreg(elr_el2); - elr -= 4; - write_sysreg(elr, elr_el2); - - return ARM_EXCEPTION_TRAP; -} - static int pkvm_handle_empty_memcache(struct pkvm_hyp_vcpu *hyp_vcpu, u64 *exit_code) { @@ -1387,27 +1363,43 @@ static int pkvm_handle_empty_memcache(struct pkvm_hyp_vcpu *hyp_vcpu, static bool pkvm_memshare_call(struct pkvm_hyp_vcpu *hyp_vcpu, u64 *exit_code) { + struct pkvm_hyp_vm *hyp_vm = pkvm_hyp_vcpu_to_hyp_vm(hyp_vcpu); struct kvm_vcpu *vcpu = &hyp_vcpu->vcpu; u64 ipa = smccc_get_arg1(vcpu); - u64 arg2 = smccc_get_arg2(vcpu); + u64 nr_pages = smccc_get_arg2(vcpu); u64 arg3 = smccc_get_arg3(vcpu); + struct kvm_hyp_req *req; + u64 nr_shared; int err; - if (arg2 || arg3) + /* Legacy guests have arg2 set to 0 */ + if (nr_pages == 0) + nr_pages = 1; + + if (arg3 || !PAGE_ALIGNED(ipa)) goto out_guest_err; - err = __pkvm_guest_share_host(hyp_vcpu, ipa); + err = __pkvm_guest_share_host(hyp_vcpu, ipa, nr_pages, &nr_shared); switch (err) { case 0: - /* Success! Now tell the host. */ - goto out_host; + atomic64_add(nr_shared * PAGE_SIZE, + &hyp_vm->host_kvm->stat.protected_shared_mem); + smccc_set_retval(vcpu, SMCCC_RET_SUCCESS, nr_shared, 0, 0); + + return true; case -EFAULT: + req = pkvm_hyp_req_reserve(hyp_vcpu, KVM_HYP_REQ_TYPE_MAP); + if (!req) + goto out_guest_err; + + req->map.guest_ipa = ipa; + req->map.size = nr_pages << PAGE_SHIFT; + /* - * Convert the exception into a data abort so that the page - * being shared is mapped into the guest next time. + * We're about to go back to the host... let's not waste time + * and check for the memcache while at it. */ - *exit_code = __pkvm_memshare_page_req(hyp_vcpu, ipa); - goto out_host; + fallthrough; case -ENOMEM: if (pkvm_handle_empty_memcache(hyp_vcpu, exit_code)) goto out_guest_err; @@ -1425,20 +1417,29 @@ out_host: static bool pkvm_memunshare_call(struct pkvm_hyp_vcpu *hyp_vcpu) { + struct pkvm_hyp_vm *hyp_vm = pkvm_hyp_vcpu_to_hyp_vm(hyp_vcpu); struct kvm_vcpu *vcpu = &hyp_vcpu->vcpu; u64 ipa = smccc_get_arg1(vcpu); - u64 arg2 = smccc_get_arg2(vcpu); + u64 nr_pages = smccc_get_arg2(vcpu); u64 arg3 = smccc_get_arg3(vcpu); + u64 nr_unshared; int err; - if (arg2 || arg3) + /* Legacy guests have arg2 set to 0 */ + if (nr_pages == 0) + nr_pages = 1; + + if (arg3 || !PAGE_ALIGNED(ipa)) goto out_guest_err; - err = __pkvm_guest_unshare_host(hyp_vcpu, ipa); + err = __pkvm_guest_unshare_host(hyp_vcpu, ipa, nr_pages, &nr_unshared); if (err) goto out_guest_err; - return false; + atomic64_add(nr_unshared * PAGE_SIZE, + &hyp_vm->host_kvm->stat.protected_shared_mem); + smccc_set_retval(vcpu, SMCCC_RET_SUCCESS, nr_unshared, 0, 0); + return true; out_guest_err: smccc_set_retval(vcpu, SMCCC_RET_INVALID_PARAMETER, 0, 0, 0); @@ -1468,11 +1469,15 @@ static bool pkvm_meminfo_call(struct pkvm_hyp_vcpu *hyp_vcpu) u64 arg1 = smccc_get_arg1(vcpu); u64 arg2 = smccc_get_arg2(vcpu); u64 arg3 = smccc_get_arg3(vcpu); + bool has_range; if (arg1 || arg2 || arg3) goto out_guest_err; - smccc_set_retval(vcpu, PAGE_SIZE, 0, 0, 0); + has_range = smccc_get_function(vcpu) == ARM_SMCCC_VENDOR_HYP_KVM_HYP_MEMINFO_FUNC_ID; + + smccc_set_retval(vcpu, PAGE_SIZE, + has_range ? KVM_FUNC_HAS_RANGE : 0, 0, 0); return true; out_guest_err: diff --git a/arch/arm64/kvm/hypercalls.c b/arch/arm64/kvm/hypercalls.c index 7bcfc93a2f1f..a92d84814d85 100644 --- a/arch/arm64/kvm/hypercalls.c +++ b/arch/arm64/kvm/hypercalls.c @@ -382,16 +382,6 @@ int kvm_smccc_call_handler(struct kvm_vcpu *vcpu) pkvm_host_reclaim_page(vcpu->kvm, smccc_get_arg1(vcpu)); val[0] = SMCCC_RET_SUCCESS; break; - case ARM_SMCCC_VENDOR_HYP_KVM_MEM_SHARE_FUNC_ID: - case ARM_SMCCC_VENDOR_HYP_KVM_MEM_UNSHARE_FUNC_ID: - if (!kvm_vm_is_protected(vcpu->kvm)) - break; - atomic64_add( - func_id == ARM_SMCCC_VENDOR_HYP_KVM_MEM_SHARE_FUNC_ID ? - PAGE_SIZE : -PAGE_SIZE, - &vcpu->kvm->stat.protected_shared_mem); - val[0] = SMCCC_RET_SUCCESS; - break; case ARM_SMCCC_TRNG_VERSION: case ARM_SMCCC_TRNG_FEATURES: case ARM_SMCCC_TRNG_GET_UUID: diff --git a/include/linux/arm-smccc.h b/include/linux/arm-smccc.h index a0b1abafc094..7b58ad2d5f74 100644 --- a/include/linux/arm-smccc.h +++ b/include/linux/arm-smccc.h @@ -182,6 +182,8 @@ #define ARM_SMCCC_KVM_FUNC_FEATURES_2 127 #define ARM_SMCCC_KVM_NUM_FUNCS 128 +#define KVM_FUNC_HAS_RANGE BIT(0) + #define ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID \ ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \ ARM_SMCCC_SMC_32, \