@@ -227,22 +227,25 @@ int ptep_set_access_flags(struct vm_area_struct *vma,
if (pte_same(pte, entry))
return 0;
- /* only preserve the access flags and write permission */
- pte_val(entry) &= PTE_RDONLY | PTE_AF | PTE_WRITE | PTE_DIRTY;
+ /* only preserve the access flags, write and exec permission */
+ pte_val(entry) &= PTE_RDONLY | PTE_AF | PTE_WRITE | PTE_DIRTY | PTE_UXN;
+
+ if (pte_user_exec(entry))
+ __sync_icache_dcache(pte);
/*
* Setting the flags must be done atomically to avoid racing with the
- * hardware update of the access/dirty state. The PTE_RDONLY bit must
- * be set to the most permissive (lowest value) of *ptep and entry
- * (calculated as: a & b == ~(~a | ~b)).
+ * hardware update of the access/dirty state. The PTE_RDONLY bit and
+ * PTE_UXN must be set to the most permissive (lowest value) of *ptep
+ * and entry (calculated as: a & b == ~(~a | ~b)).
*/
- pte_val(entry) ^= PTE_RDONLY;
+ pte_val(entry) ^= PTE_RDONLY | PTE_UXN;
pteval = pte_val(pte);
do {
old_pteval = pteval;
- pteval ^= PTE_RDONLY;
+ pteval ^= PTE_RDONLY | PTE_UXN;
pteval |= pte_val(entry);
- pteval ^= PTE_RDONLY;
+ pteval ^= PTE_RDONLY | PTE_UXN;
pteval = cmpxchg_relaxed(&pte_val(*ptep), old_pteval, pteval);
} while (pteval != old_pteval);
ptep_set_access_flags() updates page table for a mapped page entry which still got a fault probably because of a different permission than what it is mapped with. Previously an exec enabled page always gets required permission in the page table entry. Hence ptep_set_access_flags() never had to move an entry from non-exec to exec. This is going to change with deferred exec permission setting with later patches. Hence allow non-exec to exec transition here and do the required I-cache invalidation. Signed-off-by: Anshuman Khandual <anshuman.khandual@arm.com> --- arch/arm64/mm/fault.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-)