From e0a0783241aaa825bef2da8e414027367ff0949d Mon Sep 17 00:00:00 2001 From: Mostafa Saleh Date: Mon, 8 Jul 2024 12:38:42 +0000 Subject: [PATCH] ANDROID: KVM: arm64: Map MMIO in guest abort path Allow devices MMIO to be mapped in guest abort path, to avoid impacting the hot path of normal memory add the device check in the fail path of memory pinning. Bug: 357781595 Bug: 348382247 Change-Id: I2d2aae9882128b80df723042a1b6a1ce43330cd6 Signed-off-by: Mostafa Saleh --- arch/arm64/kvm/mmu.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c index e7fcb0b782b4..89cbc33692af 100644 --- a/arch/arm64/kvm/mmu.c +++ b/arch/arm64/kvm/mmu.c @@ -1716,6 +1716,11 @@ static int pkvm_relax_perms(struct kvm_vcpu *vcpu, u64 pfn, u64 gfn, u8 order, (void *)prot, false); } +static int pkvm_mem_abort_device(struct kvm *kvm, u64 pfn, u64 gfn) +{ + return kvm_call_hyp_nvhe(__pkvm_host_map_guest_mmio, pfn, gfn); +} + static int pkvm_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t *fault_ipa, struct kvm_memory_slot *memslot, size_t *size) { @@ -1751,6 +1756,33 @@ static int pkvm_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t *fault_ipa, if (ret == -EHWPOISON) { kvm_send_hwpoison_signal(hva, PAGE_SHIFT); ret = 0; + goto free_ppage; + } else if (ret == -EFAULT) { + /* + * pKVM relies on pinning the page then getting the pfn from there to map it, + * However, to avoid adding overhead on the hot path with checking pfn first, + * device check is done on the fail path for pin_user_pages, inside -EFAULT + * case, that possible is because the VMA for the device mapping is VM_IO, + * which fails in check_vma_flags() with -EFAULT + */ + bool device; + + pfn = __gfn_to_pfn_memslot(memslot, gfn, false, false, NULL, + kvm_is_write_fault(vcpu), NULL, NULL); + if (is_error_noslot_pfn(pfn)) + goto free_ppage; + device = kvm_is_device_pfn(pfn); + if (device) { + ret = pkvm_mem_abort_device(kvm, pfn, gfn); + if (ret == -EEXIST) + ret = 0; /* We might have raced with another vCPU. */ + else if (!ret && size) + *size = PAGE_SIZE; + } else { + /* Release pin from __gfn_to_pfn_memslot(). */ + kvm_release_pfn_clean(pfn); + } + goto free_ppage; } else if (ret != 1) { ret = -EFAULT;