| From d637252f72998261c9d77c0be57317c73ad77f83 Mon Sep 17 00:00:00 2001 |
| From: Laurentiu Tudor <laurentiu.tudor@nxp.com> |
| Date: Tue, 26 Jul 2016 16:38:18 +0300 |
| Subject: [PATCH] arm/arm64: KVM: drop qman mmio cacheable mapping hack |
| |
| Instead of hardcoding checks for qman cacheable |
| mmio region physical addresses extract mapping |
| information from the user-space mapping. |
| The involves several steps; |
| - get access to a pte part of the user-space mapping |
| by using get_locked_pte() / pte_unmap_unlock() apis |
| - extract memtype (normal / device), shareability from |
| the pte |
| - convert to S2 translation bits in newly added |
| function stage1_to_stage2_pgprot() |
| - finish making the s2 translation with the obtained bits |
| |
| Another explored option was using vm_area_struct::vm_page_prot |
| which is set in vfio-mc mmap code to the correct page bits. |
| However, experiments show that these bits are later altered |
| in the generic mmap code (e.g. the shareability bit is always |
| set on arm64). |
| The only place where the original bits can still be found |
| is the user-space mapping, using the method described above. |
| |
| Signed-off-by: Laurentiu Tudor <laurentiu.tudor@nxp.com> |
| [Bharat - Fixed mem_type check issue] |
| [changed "ifdef ARM64" to CONFIG_ARM64] |
| Signed-off-by: Bharat Bhushan <Bharat.Bhushan@nxp.com> |
| [Ioana - added a sanity check for hugepages] |
| Signed-off-by: Ioana Ciornei <ioana.ciornei@nxp.com> |
| [Fixed format issues] |
| Signed-off-by: Diana Craciun <diana.craciun@nxp.com> |
| --- |
| virt/kvm/arm/mmu.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- |
| 1 file changed, 53 insertions(+), 2 deletions(-) |
| |
| --- a/virt/kvm/arm/mmu.c |
| +++ b/virt/kvm/arm/mmu.c |
| @@ -1381,6 +1381,30 @@ out: |
| return ret; |
| } |
| |
| +#ifdef CONFIG_ARM64 |
| +static pgprot_t stage1_to_stage2_pgprot(pgprot_t prot) |
| +{ |
| + switch (pgprot_val(prot) & PTE_ATTRINDX_MASK) { |
| + case PTE_ATTRINDX(MT_DEVICE_nGnRE): |
| + case PTE_ATTRINDX(MT_DEVICE_nGnRnE): |
| + case PTE_ATTRINDX(MT_DEVICE_GRE): |
| + return PAGE_S2_DEVICE; |
| + case PTE_ATTRINDX(MT_NORMAL_NC): |
| + case PTE_ATTRINDX(MT_NORMAL): |
| + return (pgprot_val(prot) & PTE_SHARED) |
| + ? PAGE_S2 |
| + : PAGE_S2_NS; |
| + } |
| + |
| + return PAGE_S2_DEVICE; |
| +} |
| +#else |
| +static pgprot_t stage1_to_stage2_pgprot(pgprot_t prot) |
| +{ |
| + return PAGE_S2_DEVICE; |
| +} |
| +#endif |
| + |
| static bool transparent_hugepage_adjust(kvm_pfn_t *pfnp, phys_addr_t *ipap) |
| { |
| kvm_pfn_t pfn = *pfnp; |
| @@ -1725,8 +1749,23 @@ static int user_mem_abort(struct kvm_vcp |
| * 3 levels, i.e, PMD is not folded. |
| */ |
| if (vma_pagesize == PMD_SIZE || |
| - (vma_pagesize == PUD_SIZE && kvm_stage2_has_pmd(kvm))) |
| + (vma_pagesize == PUD_SIZE && kvm_stage2_has_pmd(kvm))) { |
| gfn = (fault_ipa & huge_page_mask(hstate_vma(vma))) >> PAGE_SHIFT; |
| + } else { |
| + if (!is_vm_hugetlb_page(vma)) { |
| + pte_t *pte; |
| + spinlock_t *ptl; |
| + pgprot_t prot; |
| + |
| + pte = get_locked_pte(current->mm, memslot->userspace_addr, &ptl); |
| + prot = stage1_to_stage2_pgprot(__pgprot(pte_val(*pte))); |
| + pte_unmap_unlock(pte, ptl); |
| +#ifdef CONFIG_ARM64 |
| + if (pgprot_val(prot) == pgprot_val(PAGE_S2_NS)) |
| + mem_type = PAGE_S2_NS; |
| +#endif |
| + } |
| + } |
| up_read(¤t->mm->mmap_sem); |
| |
| /* We need minimum second+third level pages */ |
| @@ -1755,6 +1794,11 @@ static int user_mem_abort(struct kvm_vcp |
| if (is_error_noslot_pfn(pfn)) |
| return -EFAULT; |
| |
| +#ifdef CONFIG_ARM64 |
| + if (pgprot_val(mem_type) == pgprot_val(PAGE_S2_NS)) { |
| + flags |= KVM_S2PTE_FLAG_IS_IOMAP; |
| + } else |
| +#endif |
| if (kvm_is_device_pfn(pfn)) { |
| mem_type = PAGE_S2_DEVICE; |
| flags |= KVM_S2PTE_FLAG_IS_IOMAP; |
| @@ -2351,6 +2395,9 @@ int kvm_arch_prepare_memory_region(struc |
| gpa_t gpa = mem->guest_phys_addr + |
| (vm_start - mem->userspace_addr); |
| phys_addr_t pa; |
| + pgprot_t prot; |
| + pte_t *pte; |
| + spinlock_t *ptl; |
| |
| pa = (phys_addr_t)vma->vm_pgoff << PAGE_SHIFT; |
| pa += vm_start - vma->vm_start; |
| @@ -2361,9 +2408,13 @@ int kvm_arch_prepare_memory_region(struc |
| goto out; |
| } |
| |
| + pte = get_locked_pte(current->mm, mem->userspace_addr, &ptl); |
| + prot = stage1_to_stage2_pgprot(__pgprot(pte_val(*pte))); |
| + pte_unmap_unlock(pte, ptl); |
| + |
| ret = kvm_phys_addr_ioremap(kvm, gpa, pa, |
| vm_end - vm_start, |
| - writable, PAGE_S2_DEVICE); |
| + writable, prot); |
| if (ret) |
| break; |
| } |