Skip to content
  • Paul Mackerras's avatar
    KVM: PPC: Book3S: Fix guest DMA when guest partially backed by THP pages · 025cc91f
    Paul Mackerras authored
    commit 8cfbdbdc upstream.
    
    Commit 76fa4975 ("KVM: PPC: Check if IOMMU page is contained in
    the pinned physical page", 2018-07-17) added some checks to ensure
    that guest DMA mappings don't attempt to map more than the guest is
    entitled to access. However, errors in the logic mean that legitimate
    guest requests to map pages for DMA are being denied in some
    situations. Specifically, if the first page of the range passed to
    mm_iommu_get() is mapped with a normal page, and subsequent pages are
    mapped with transparent huge pages, we end up with mem->pageshift ==
    0. That means that the page size checks in mm_iommu_ua_to_hpa() and
    mm_iommu_up_to_hpa_rm() will always fail for every page in that
    region, and thus the guest can never map any memory in that region for
    DMA, typically leading to a flood of error messages like this:
    
      qemu-system-ppc64: VFIO_MAP_DMA: -22
      qemu-system-ppc64: vfio_dma_map(0x10005f47780, 0x800000000000000, 0x10000, 0x7fff63ff0000) = -22 (Invalid argument)
    
    The logic errors in mm_iommu_get() are:
    
      (a) use of 'ua' not 'ua + (i << PAGE_SHIFT)' in the find_linux_pte()
          call (meaning that find_linux_pte() returns the pte for the
          first address in the range, not the address we are currently up
          to);
      (b) use of 'pageshift' as the variable to receive the hugepage shift
          returned by find_linux_pte() - for a normal page this gets set
          to 0, leading to us setting mem->pageshift to 0 when we conclude
          that the pte returned by find_linux_pte() didn't match the page
          we were looking at;
      (c) comparing 'compshift', which is a page order, i.e. log base 2 of
          the number of pages, with 'pageshift', which is a log base 2 of
          the number of bytes.
    
    To fix these problems, this patch introduces 'cur_ua' to hold the
    current user address and uses that in the find_linux_pte() call;
    introduces 'pteshift' to hold the hugepage shift found by
    find_linux_pte(); and compares 'pteshift' with 'compshift +
    PAGE_SHIFT' rather than 'compshift'.
    
    The patch also moves the local_irq_restore to the point after the PTE
    pointer returned by find_linux_pte() has been dereferenced because
    otherwise the PTE could change underneath us, and adds a check to
    avoid doing the find_linux_pte() call once mem->pageshift has been
    reduced to PAGE_SHIFT, as an optimization.
    
    Fixes: 76fa4975
    
     ("KVM: PPC: Check if IOMMU page is contained in the pinned physical page")
    Cc: stable@vger.kernel.org # v4.12+
    Signed-off-by: default avatarPaul Mackerras <paulus@ozlabs.org>
    Signed-off-by: default avatarMichael Ellerman <mpe@ellerman.id.au>
    Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
    025cc91f