Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

powerpc/nohash: Fix __ptep_set_access_flags() and ptep_set_wrprotect()

Commit 26973fa5ac0e ("powerpc/mm: use pte helpers in generic code")
changed those two functions to use pte helpers to determine which
bits to clear and which bits to set.

This change was based on the assumption that bits to be set/cleared
are always the same and can be determined by applying the pte
manipulation helpers on __pte(0).

But on platforms like book3e, the bits depend on whether the page
is a user page or not.

For the time being it more or less works because of _PAGE_EXEC being
used for user pages only and exec right being set at all time on
kernel page. But following patch will clean that and output of
pte_mkexec() will depend on the page being a user or kernel page.

Instead of trying to make an even more complicated helper where bits
would become dependent on the final pte value, come back to a more
static situation like before commit 26973fa5ac0e ("powerpc/mm: use
pte helpers in generic code"), by introducing an 8xx specific
version of __ptep_set_access_flags() and ptep_set_wrprotect().

Fixes: 26973fa5ac0e ("powerpc/mm: use pte helpers in generic code")
Signed-off-by: Christophe Leroy <christophe.leroy@csgroup.eu>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/922bdab3a220781bae2360ff3dd5adb7fe4d34f1.1635226743.git.christophe.leroy@csgroup.eu

authored by

Christophe Leroy and committed by
Michael Ellerman
b1b93cb7 44a8214d

+30 -9
+8 -9
arch/powerpc/include/asm/nohash/32/pgtable.h
··· 306 306 } 307 307 308 308 #define __HAVE_ARCH_PTEP_SET_WRPROTECT 309 + #ifndef ptep_set_wrprotect 309 310 static inline void ptep_set_wrprotect(struct mm_struct *mm, unsigned long addr, 310 311 pte_t *ptep) 311 312 { 312 - unsigned long clr = ~pte_val(pte_wrprotect(__pte(~0))); 313 - unsigned long set = pte_val(pte_wrprotect(__pte(0))); 314 - 315 - pte_update(mm, addr, ptep, clr, set, 0); 313 + pte_update(mm, addr, ptep, _PAGE_RW, 0, 0); 316 314 } 315 + #endif 317 316 317 + #ifndef __ptep_set_access_flags 318 318 static inline void __ptep_set_access_flags(struct vm_area_struct *vma, 319 319 pte_t *ptep, pte_t entry, 320 320 unsigned long address, 321 321 int psize) 322 322 { 323 - pte_t pte_set = pte_mkyoung(pte_mkdirty(pte_mkwrite(pte_mkexec(__pte(0))))); 324 - pte_t pte_clr = pte_mkyoung(pte_mkdirty(pte_mkwrite(pte_mkexec(__pte(~0))))); 325 - unsigned long set = pte_val(entry) & pte_val(pte_set); 326 - unsigned long clr = ~pte_val(entry) & ~pte_val(pte_clr); 323 + unsigned long set = pte_val(entry) & 324 + (_PAGE_DIRTY | _PAGE_ACCESSED | _PAGE_RW | _PAGE_EXEC); 327 325 int huge = psize > mmu_virtual_psize ? 1 : 0; 328 326 329 - pte_update(vma->vm_mm, address, ptep, clr, set, huge); 327 + pte_update(vma->vm_mm, address, ptep, 0, set, huge); 330 328 331 329 flush_tlb_page(vma, address); 332 330 } 331 + #endif 333 332 334 333 static inline int pte_young(pte_t pte) 335 334 {
+22
arch/powerpc/include/asm/nohash/32/pte-8xx.h
··· 136 136 137 137 #define pte_mkhuge pte_mkhuge 138 138 139 + static inline pte_basic_t pte_update(struct mm_struct *mm, unsigned long addr, pte_t *p, 140 + unsigned long clr, unsigned long set, int huge); 141 + 142 + static inline void ptep_set_wrprotect(struct mm_struct *mm, unsigned long addr, pte_t *ptep) 143 + { 144 + pte_update(mm, addr, ptep, 0, _PAGE_RO, 0); 145 + } 146 + #define ptep_set_wrprotect ptep_set_wrprotect 147 + 148 + static inline void __ptep_set_access_flags(struct vm_area_struct *vma, pte_t *ptep, 149 + pte_t entry, unsigned long address, int psize) 150 + { 151 + unsigned long set = pte_val(entry) & (_PAGE_DIRTY | _PAGE_ACCESSED | _PAGE_EXEC); 152 + unsigned long clr = ~pte_val(entry) & _PAGE_RO; 153 + int huge = psize > mmu_virtual_psize ? 1 : 0; 154 + 155 + pte_update(vma->vm_mm, address, ptep, clr, set, huge); 156 + 157 + flush_tlb_page(vma, address); 158 + } 159 + #define __ptep_set_access_flags __ptep_set_access_flags 160 + 139 161 static inline unsigned long pgd_leaf_size(pgd_t pgd) 140 162 { 141 163 if (pgd_val(pgd) & _PMD_PAGE_8M)