Merge branch 'misc.namei' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
[linux-2.6-microblaze.git] / arch / arm64 / kvm / mmu.c
index 0625bf2..1a94a7c 100644 (file)
@@ -80,6 +80,7 @@ static bool memslot_is_logging(struct kvm_memory_slot *memslot)
  */
 void kvm_flush_remote_tlbs(struct kvm *kvm)
 {
+       ++kvm->stat.generic.remote_tlb_flush_requests;
        kvm_call_hyp(__kvm_tlb_flush_vmid, &kvm->arch.mmu);
 }
 
@@ -259,10 +260,8 @@ static int __create_hyp_mappings(unsigned long start, unsigned long size,
 {
        int err;
 
-       if (!kvm_host_owns_hyp_mappings()) {
-               return kvm_call_hyp_nvhe(__pkvm_create_mappings,
-                                        start, size, phys, prot);
-       }
+       if (WARN_ON(!kvm_host_owns_hyp_mappings()))
+               return -EINVAL;
 
        mutex_lock(&kvm_hyp_pgd_mutex);
        err = kvm_pgtable_hyp_map(hyp_pgtable, start, size, phys, prot);
@@ -282,6 +281,21 @@ static phys_addr_t kvm_kaddr_to_phys(void *kaddr)
        }
 }
 
+static int pkvm_share_hyp(phys_addr_t start, phys_addr_t end)
+{
+       phys_addr_t addr;
+       int ret;
+
+       for (addr = ALIGN_DOWN(start, PAGE_SIZE); addr < end; addr += PAGE_SIZE) {
+               ret = kvm_call_hyp_nvhe(__pkvm_host_share_hyp,
+                                       __phys_to_pfn(addr));
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
 /**
  * create_hyp_mappings - duplicate a kernel virtual address range in Hyp mode
  * @from:      The virtual kernel start address of the range
@@ -302,6 +316,13 @@ int create_hyp_mappings(void *from, void *to, enum kvm_pgtable_prot prot)
        if (is_kernel_in_hyp_mode())
                return 0;
 
+       if (!kvm_host_owns_hyp_mappings()) {
+               if (WARN_ON(prot != PAGE_HYP))
+                       return -EPERM;
+               return pkvm_share_hyp(kvm_kaddr_to_phys(from),
+                                     kvm_kaddr_to_phys(to));
+       }
+
        start = start & PAGE_MASK;
        end = PAGE_ALIGN(end);
 
@@ -433,6 +454,32 @@ int create_hyp_exec_mappings(phys_addr_t phys_addr, size_t size,
        return 0;
 }
 
+static struct kvm_pgtable_mm_ops kvm_user_mm_ops = {
+       /* We shouldn't need any other callback to walk the PT */
+       .phys_to_virt           = kvm_host_va,
+};
+
+static int get_user_mapping_size(struct kvm *kvm, u64 addr)
+{
+       struct kvm_pgtable pgt = {
+               .pgd            = (kvm_pte_t *)kvm->mm->pgd,
+               .ia_bits        = VA_BITS,
+               .start_level    = (KVM_PGTABLE_MAX_LEVELS -
+                                  CONFIG_PGTABLE_LEVELS),
+               .mm_ops         = &kvm_user_mm_ops,
+       };
+       kvm_pte_t pte = 0;      /* Keep GCC quiet... */
+       u32 level = ~0;
+       int ret;
+
+       ret = kvm_pgtable_get_leaf(&pgt, addr, &pte, &level);
+       VM_BUG_ON(ret);
+       VM_BUG_ON(level >= KVM_PGTABLE_MAX_LEVELS);
+       VM_BUG_ON(!(pte & PTE_VALID));
+
+       return BIT(ARM64_HW_PGTABLE_LEVEL_SHIFT(level));
+}
+
 static struct kvm_pgtable_mm_ops kvm_s2_mm_ops = {
        .zalloc_page            = stage2_memcache_zalloc_page,
        .zalloc_pages_exact     = kvm_host_zalloc_pages_exact,
@@ -485,7 +532,7 @@ int kvm_init_stage2_mmu(struct kvm *kvm, struct kvm_s2_mmu *mmu)
        mmu->arch = &kvm->arch;
        mmu->pgt = pgt;
        mmu->pgd_phys = __pa(pgt->pgd);
-       mmu->vmid.vmid_gen = 0;
+       WRITE_ONCE(mmu->vmid.vmid_gen, 0);
        return 0;
 
 out_destroy_pgtable:
@@ -780,7 +827,7 @@ static bool fault_supports_stage2_huge_mapping(struct kvm_memory_slot *memslot,
  * Returns the size of the mapping.
  */
 static unsigned long
-transparent_hugepage_adjust(struct kvm_memory_slot *memslot,
+transparent_hugepage_adjust(struct kvm *kvm, struct kvm_memory_slot *memslot,
                            unsigned long hva, kvm_pfn_t *pfnp,
                            phys_addr_t *ipap)
 {
@@ -791,8 +838,8 @@ transparent_hugepage_adjust(struct kvm_memory_slot *memslot,
         * sure that the HVA and IPA are sufficiently aligned and that the
         * block map is contained within the memslot.
         */
-       if (kvm_is_transparent_hugepage(pfn) &&
-           fault_supports_stage2_huge_mapping(memslot, hva, PMD_SIZE)) {
+       if (fault_supports_stage2_huge_mapping(memslot, hva, PMD_SIZE) &&
+           get_user_mapping_size(kvm, hva) >= PMD_SIZE) {
                /*
                 * The address we faulted on is backed by a transparent huge
                 * page.  However, because we map the compound huge page and
@@ -814,7 +861,7 @@ transparent_hugepage_adjust(struct kvm_memory_slot *memslot,
                *ipap &= PMD_MASK;
                kvm_release_pfn_clean(pfn);
                pfn &= ~(PTRS_PER_PMD - 1);
-               kvm_get_pfn(pfn);
+               get_page(pfn_to_page(pfn));
                *pfnp = pfn;
 
                return PMD_SIZE;
@@ -1050,9 +1097,14 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
         * If we are not forced to use page mapping, check if we are
         * backed by a THP and thus use block mapping if possible.
         */
-       if (vma_pagesize == PAGE_SIZE && !(force_pte || device))
-               vma_pagesize = transparent_hugepage_adjust(memslot, hva,
-                                                          &pfn, &fault_ipa);
+       if (vma_pagesize == PAGE_SIZE && !(force_pte || device)) {
+               if (fault_status == FSC_PERM && fault_granule > PAGE_SIZE)
+                       vma_pagesize = fault_granule;
+               else
+                       vma_pagesize = transparent_hugepage_adjust(kvm, memslot,
+                                                                  hva, &pfn,
+                                                                  &fault_ipa);
+       }
 
        if (fault_status != FSC_PERM && !device && kvm_has_mte(kvm)) {
                /* Check the VMM hasn't introduced a new VM_SHARED VMA */