mm_ops->put_page(ptep);
 }
 
+static bool stage2_pte_cacheable(struct kvm_pgtable *pgt, kvm_pte_t pte)
+{
+       u64 memattr = pte & KVM_PTE_LEAF_ATTR_LO_S2_MEMATTR;
+       return memattr == KVM_S2_MEMATTR(pgt, NORMAL);
+}
+
+static bool stage2_pte_executable(kvm_pte_t pte)
+{
+       return !(pte & KVM_PTE_LEAF_ATTR_HI_S2_XN);
+}
+
 static int stage2_map_walker_try_leaf(u64 addr, u64 end, u32 level,
                                      kvm_pte_t *ptep,
                                      struct stage2_map_data *data)
 {
        kvm_pte_t new, old = *ptep;
        u64 granule = kvm_granule_size(level), phys = data->phys;
+       struct kvm_pgtable *pgt = data->mmu->pgt;
        struct kvm_pgtable_mm_ops *mm_ops = data->mm_ops;
 
        if (!kvm_block_mapping_supported(addr, end, phys, level))
                stage2_put_pte(ptep, data->mmu, addr, level, mm_ops);
        }
 
+       /* Perform CMOs before installation of the guest stage-2 PTE */
+       if (mm_ops->dcache_clean_inval_poc && stage2_pte_cacheable(pgt, new))
+               mm_ops->dcache_clean_inval_poc(kvm_pte_follow(new, mm_ops),
+                                               granule);
+
+       if (mm_ops->icache_inval_pou && stage2_pte_executable(new))
+               mm_ops->icache_inval_pou(kvm_pte_follow(new, mm_ops), granule);
+
        smp_store_release(ptep, new);
        if (stage2_pte_is_counted(new))
                mm_ops->get_page(ptep);
        return ret;
 }
 
-static bool stage2_pte_cacheable(struct kvm_pgtable *pgt, kvm_pte_t pte)
-{
-       u64 memattr = pte & KVM_PTE_LEAF_ATTR_LO_S2_MEMATTR;
-       return memattr == KVM_S2_MEMATTR(pgt, NORMAL);
-}
-
 static int stage2_unmap_walker(u64 addr, u64 end, u32 level, kvm_pte_t *ptep,
                               enum kvm_pgtable_walk_flags flag,
                               void * const arg)
 {
        kvm_pte_t pte = *ptep;
        struct stage2_attr_data *data = arg;
+       struct kvm_pgtable_mm_ops *mm_ops = data->mm_ops;
 
        if (!kvm_pte_valid(pte))
                return 0;
         * but worst-case the access flag update gets lost and will be
         * set on the next access instead.
         */
-       if (data->pte != pte)
+       if (data->pte != pte) {
+               /*
+                * Invalidate instruction cache before updating the guest
+                * stage-2 PTE if we are going to add executable permission.
+                */
+               if (mm_ops->icache_inval_pou &&
+                   stage2_pte_executable(pte) && !stage2_pte_executable(*ptep))
+                       mm_ops->icache_inval_pou(kvm_pte_follow(pte, mm_ops),
+                                                 kvm_granule_size(level));
                WRITE_ONCE(*ptep, pte);
+       }
 
        return 0;
 }
 
        .page_count             = kvm_host_page_count,
        .phys_to_virt           = kvm_host_va,
        .virt_to_phys           = kvm_host_pa,
+       .dcache_clean_inval_poc = clean_dcache_guest_page,
+       .icache_inval_pou       = invalidate_icache_guest_page,
 };
 
 /**
        if (writable)
                prot |= KVM_PGTABLE_PROT_W;
 
-       if (fault_status != FSC_PERM && !device)
-               clean_dcache_guest_page(page_address(pfn_to_page(pfn)),
-                                       vma_pagesize);
-
-       if (exec_fault) {
+       if (exec_fault)
                prot |= KVM_PGTABLE_PROT_X;
-               invalidate_icache_guest_page(page_address(pfn_to_page(pfn)),
-                                            vma_pagesize);
-       }
 
        if (device)
                prot |= KVM_PGTABLE_PROT_DEVICE;
        WARN_ON(range->end - range->start != 1);
 
        /*
-        * We've moved a page around, probably through CoW, so let's treat it
-        * just like a translation fault and clean the cache to the PoC.
-        */
-       clean_dcache_guest_page(page_address(pfn_to_page(pfn)), PAGE_SIZE);
-
-       /*
+        * We've moved a page around, probably through CoW, so let's treat
+        * it just like a translation fault and the map handler will clean
+        * the cache to the PoC.
+        *
         * The MMU notifiers will have unmapped a huge PMD before calling
         * ->change_pte() (which in turn calls kvm_set_spte_gfn()) and
         * therefore we never need to clear out a huge PMD through this