From bd55bd84082e4882aaeff13ebfdcb78b329f5302 Mon Sep 17 00:00:00 2001 From: Mostafa Saleh Date: Fri, 7 Mar 2025 15:42:17 +0000 Subject: [PATCH] ANDROID: KVM: arm64: Generalize guest PA query Soon, pvIOMMU would want to map pages from IOVA -> PA, using IPA, so the hypervisor would need to query the PA for this pages and possibly request to map them, this is very similar to the logic used in device token requests. So, reuse the code in a more generic way that is range friendly. Bug: 357781595 Bug: 348382247 Bug: 236685427 Change-Id: Ic0235d97a9722b75c8583dc8550025fa796eda43 Signed-off-by: Mostafa Saleh --- arch/arm64/kvm/hyp/include/nvhe/mem_protect.h | 4 +- arch/arm64/kvm/hyp/nvhe/device/device.c | 30 +++--------- arch/arm64/kvm/hyp/nvhe/mem_protect.c | 48 ++++++++++++++----- 3 files changed, 45 insertions(+), 37 deletions(-) diff --git a/arch/arm64/kvm/hyp/include/nvhe/mem_protect.h b/arch/arm64/kvm/hyp/include/nvhe/mem_protect.h index ae14492be415..db48cfb89e00 100644 --- a/arch/arm64/kvm/hyp/include/nvhe/mem_protect.h +++ b/arch/arm64/kvm/hyp/include/nvhe/mem_protect.h @@ -80,7 +80,9 @@ u64 __pkvm_ptdump_walk_range(pkvm_handle_t handle, struct pkvm_ptdump_log_hdr *l int hyp_check_range_owned(u64 addr, u64 size); int __pkvm_install_guest_mmio(struct pkvm_hyp_vcpu *hyp_vcpu, u64 pfn, u64 gfn); -int __pkvm_guest_get_valid_phys_page(struct pkvm_hyp_vm *vm, u64 *phys, u64 ipa); +int pkvm_get_guest_pa_request(struct pkvm_hyp_vcpu *hyp_vcpu, u64 ipa, + size_t ipa_size_request, u64 *out_pa, s8 *out_level, + u64 *exit_code); bool addr_is_memory(phys_addr_t phys); int host_stage2_idmap_locked(phys_addr_t addr, u64 size, diff --git a/arch/arm64/kvm/hyp/nvhe/device/device.c b/arch/arm64/kvm/hyp/nvhe/device/device.c index dfcc6ec221c0..2a2efa2a2ab4 100644 --- a/arch/arm64/kvm/hyp/nvhe/device/device.c +++ b/arch/arm64/kvm/hyp/nvhe/device/device.c @@ -267,29 +267,6 @@ out_ret: return ret; } -static int pkvm_get_device_pa(struct pkvm_hyp_vcpu *hyp_vcpu, u64 ipa, u64 *pa, u64 *exit_code) -{ - struct kvm_hyp_req *req; - int ret; - struct pkvm_hyp_vm *vm = pkvm_hyp_vcpu_to_hyp_vm(hyp_vcpu); - - ret = __pkvm_guest_get_valid_phys_page(vm, pa, ipa); - if (ret == -ENOENT) { - /* Page not mapped, create a request*/ - req = pkvm_hyp_req_reserve(hyp_vcpu, KVM_HYP_REQ_TYPE_MAP); - if (!req) - return -ENOMEM; - - req->map.guest_ipa = ipa; - req->map.size = PAGE_SIZE; - *exit_code = ARM_EXCEPTION_HYP_REQ; - /* Repeat next time. */ - write_sysreg_el2(read_sysreg_el2(SYS_ELR) - 4, SYS_ELR); - } - - return ret; -} - bool pkvm_device_request_mmio(struct pkvm_hyp_vcpu *hyp_vcpu, u64 *exit_code) { int i, j, ret; @@ -297,17 +274,22 @@ bool pkvm_device_request_mmio(struct pkvm_hyp_vcpu *hyp_vcpu, u64 *exit_code) struct pkvm_hyp_vm *vm = pkvm_hyp_vcpu_to_hyp_vm(hyp_vcpu); u64 ipa = smccc_get_arg1(vcpu); u64 token; + s8 level; /* arg2 and arg3 reserved for future use. */ if (smccc_get_arg2(vcpu) || smccc_get_arg3(vcpu) || !PAGE_ALIGNED(ipa)) goto out_inval; - ret = pkvm_get_device_pa(hyp_vcpu, ipa, &token, exit_code); + ret = pkvm_get_guest_pa_request(hyp_vcpu, ipa, PAGE_SIZE, + &token, &level, exit_code); if (ret == -ENOENT) return false; else if (ret) goto out_inval; + /* It's expected the address is mapped as page for MMIO */ + WARN_ON(level != KVM_PGTABLE_LAST_LEVEL); + hyp_spin_lock(&device_spinlock); for (i = 0 ; i < registered_devices_nr ; ++i) { struct pkvm_device *dev = ®istered_devices[i]; diff --git a/arch/arm64/kvm/hyp/nvhe/mem_protect.c b/arch/arm64/kvm/hyp/nvhe/mem_protect.c index e5470cc98d68..47604640c32c 100644 --- a/arch/arm64/kvm/hyp/nvhe/mem_protect.c +++ b/arch/arm64/kvm/hyp/nvhe/mem_protect.c @@ -2105,18 +2105,6 @@ static int guest_get_valid_pte(struct pkvm_hyp_vm *vm, u64 *phys, u64 ipa, u8 or return 0; } -int __pkvm_guest_get_valid_phys_page(struct pkvm_hyp_vm *vm, u64 *phys, u64 ipa) -{ - kvm_pte_t pte; - int ret; - - guest_lock_component(vm); - ret = guest_get_valid_pte(vm, phys, ipa, 0, &pte); - guest_unlock_component(vm); - - return ret; -} - int __pkvm_host_relax_perms_guest(u64 gfn, struct pkvm_hyp_vcpu *vcpu, enum kvm_pgtable_prot prot) { struct pkvm_hyp_vm *vm = pkvm_hyp_vcpu_to_hyp_vm(vcpu); @@ -2743,6 +2731,42 @@ teardown: return ret; } +/* Return PA for an owned guest IPA or request it, and repeat the guest HVC */ +int pkvm_get_guest_pa_request(struct pkvm_hyp_vcpu *hyp_vcpu, u64 ipa, + size_t ipa_size_request, u64 *out_pa, s8 *out_level, + u64 *exit_code) +{ + struct kvm_hyp_req *req; + kvm_pte_t pte; + enum pkvm_page_state state; + struct pkvm_hyp_vm *vm = pkvm_hyp_vcpu_to_hyp_vm(hyp_vcpu); + + guest_lock_component(vm); + WARN_ON(kvm_pgtable_get_leaf(&vm->pgt, ipa, &pte, out_level)); + guest_unlock_component(vm); + if (!kvm_pte_valid(pte)) { + /* Page not mapped, create a request*/ + req = pkvm_hyp_req_reserve(hyp_vcpu, KVM_HYP_REQ_TYPE_MAP); + if (!req) + return -ENOMEM; + + req->map.guest_ipa = ipa; + req->map.size = ipa_size_request; + *exit_code = ARM_EXCEPTION_HYP_REQ; + /* Repeat next time. */ + write_sysreg_el2(read_sysreg_el2(SYS_ELR) - 4, SYS_ELR); + return -ENOENT; + } + + state = pkvm_getstate(kvm_pgtable_stage2_pte_prot(pte)); + if (state != PKVM_PAGE_OWNED) + return -EPERM; + + *out_pa = kvm_pte_to_phys(pte); + *out_pa |= (ipa & kvm_granule_size(*out_level) - 1) & PAGE_MASK; + return 0; +} + #ifdef CONFIG_PKVM_SELFTESTS struct pkvm_expected_state { enum pkvm_page_state host;