162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0-only */ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2012 ARM Ltd. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci#ifndef __ASM_PGTABLE_H 662306a36Sopenharmony_ci#define __ASM_PGTABLE_H 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <asm/bug.h> 962306a36Sopenharmony_ci#include <asm/proc-fns.h> 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <asm/memory.h> 1262306a36Sopenharmony_ci#include <asm/mte.h> 1362306a36Sopenharmony_ci#include <asm/pgtable-hwdef.h> 1462306a36Sopenharmony_ci#include <asm/pgtable-prot.h> 1562306a36Sopenharmony_ci#include <asm/tlbflush.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci/* 1862306a36Sopenharmony_ci * VMALLOC range. 1962306a36Sopenharmony_ci * 2062306a36Sopenharmony_ci * VMALLOC_START: beginning of the kernel vmalloc space 2162306a36Sopenharmony_ci * VMALLOC_END: extends to the available space below vmemmap, PCI I/O space 2262306a36Sopenharmony_ci * and fixed mappings 2362306a36Sopenharmony_ci */ 2462306a36Sopenharmony_ci#define VMALLOC_START (MODULES_END) 2562306a36Sopenharmony_ci#define VMALLOC_END (VMEMMAP_START - SZ_256M) 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#define vmemmap ((struct page *)VMEMMAP_START - (memstart_addr >> PAGE_SHIFT)) 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#ifndef __ASSEMBLY__ 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#include <asm/cmpxchg.h> 3262306a36Sopenharmony_ci#include <asm/fixmap.h> 3362306a36Sopenharmony_ci#include <linux/mmdebug.h> 3462306a36Sopenharmony_ci#include <linux/mm_types.h> 3562306a36Sopenharmony_ci#include <linux/sched.h> 3662306a36Sopenharmony_ci#include <linux/page_table_check.h> 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#ifdef CONFIG_TRANSPARENT_HUGEPAGE 3962306a36Sopenharmony_ci#define __HAVE_ARCH_FLUSH_PMD_TLB_RANGE 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci/* Set stride and tlb_level in flush_*_tlb_range */ 4262306a36Sopenharmony_ci#define flush_pmd_tlb_range(vma, addr, end) \ 4362306a36Sopenharmony_ci __flush_tlb_range(vma, addr, end, PMD_SIZE, false, 2) 4462306a36Sopenharmony_ci#define flush_pud_tlb_range(vma, addr, end) \ 4562306a36Sopenharmony_ci __flush_tlb_range(vma, addr, end, PUD_SIZE, false, 1) 4662306a36Sopenharmony_ci#endif /* CONFIG_TRANSPARENT_HUGEPAGE */ 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic inline bool arch_thp_swp_supported(void) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci return !system_supports_mte(); 5162306a36Sopenharmony_ci} 5262306a36Sopenharmony_ci#define arch_thp_swp_supported arch_thp_swp_supported 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci/* 5562306a36Sopenharmony_ci * Outside of a few very special situations (e.g. hibernation), we always 5662306a36Sopenharmony_ci * use broadcast TLB invalidation instructions, therefore a spurious page 5762306a36Sopenharmony_ci * fault on one CPU which has been handled concurrently by another CPU 5862306a36Sopenharmony_ci * does not need to perform additional invalidation. 5962306a36Sopenharmony_ci */ 6062306a36Sopenharmony_ci#define flush_tlb_fix_spurious_fault(vma, address, ptep) do { } while (0) 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci/* 6362306a36Sopenharmony_ci * ZERO_PAGE is a global shared page that is always zero: used 6462306a36Sopenharmony_ci * for zero-mapped memory areas etc.. 6562306a36Sopenharmony_ci */ 6662306a36Sopenharmony_ciextern unsigned long empty_zero_page[PAGE_SIZE / sizeof(unsigned long)]; 6762306a36Sopenharmony_ci#define ZERO_PAGE(vaddr) phys_to_page(__pa_symbol(empty_zero_page)) 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci#define pte_ERROR(e) \ 7062306a36Sopenharmony_ci pr_err("%s:%d: bad pte %016llx.\n", __FILE__, __LINE__, pte_val(e)) 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci/* 7362306a36Sopenharmony_ci * Macros to convert between a physical address and its placement in a 7462306a36Sopenharmony_ci * page table entry, taking care of 52-bit addresses. 7562306a36Sopenharmony_ci */ 7662306a36Sopenharmony_ci#ifdef CONFIG_ARM64_PA_BITS_52 7762306a36Sopenharmony_cistatic inline phys_addr_t __pte_to_phys(pte_t pte) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci return (pte_val(pte) & PTE_ADDR_LOW) | 8062306a36Sopenharmony_ci ((pte_val(pte) & PTE_ADDR_HIGH) << PTE_ADDR_HIGH_SHIFT); 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_cistatic inline pteval_t __phys_to_pte_val(phys_addr_t phys) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci return (phys | (phys >> PTE_ADDR_HIGH_SHIFT)) & PTE_ADDR_MASK; 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ci#else 8762306a36Sopenharmony_ci#define __pte_to_phys(pte) (pte_val(pte) & PTE_ADDR_MASK) 8862306a36Sopenharmony_ci#define __phys_to_pte_val(phys) (phys) 8962306a36Sopenharmony_ci#endif 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci#define pte_pfn(pte) (__pte_to_phys(pte) >> PAGE_SHIFT) 9262306a36Sopenharmony_ci#define pfn_pte(pfn,prot) \ 9362306a36Sopenharmony_ci __pte(__phys_to_pte_val((phys_addr_t)(pfn) << PAGE_SHIFT) | pgprot_val(prot)) 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci#define pte_none(pte) (!pte_val(pte)) 9662306a36Sopenharmony_ci#define pte_clear(mm,addr,ptep) set_pte(ptep, __pte(0)) 9762306a36Sopenharmony_ci#define pte_page(pte) (pfn_to_page(pte_pfn(pte))) 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci/* 10062306a36Sopenharmony_ci * The following only work if pte_present(). Undefined behaviour otherwise. 10162306a36Sopenharmony_ci */ 10262306a36Sopenharmony_ci#define pte_present(pte) (!!(pte_val(pte) & (PTE_VALID | PTE_PROT_NONE))) 10362306a36Sopenharmony_ci#define pte_young(pte) (!!(pte_val(pte) & PTE_AF)) 10462306a36Sopenharmony_ci#define pte_special(pte) (!!(pte_val(pte) & PTE_SPECIAL)) 10562306a36Sopenharmony_ci#define pte_write(pte) (!!(pte_val(pte) & PTE_WRITE)) 10662306a36Sopenharmony_ci#define pte_rdonly(pte) (!!(pte_val(pte) & PTE_RDONLY)) 10762306a36Sopenharmony_ci#define pte_user(pte) (!!(pte_val(pte) & PTE_USER)) 10862306a36Sopenharmony_ci#define pte_user_exec(pte) (!(pte_val(pte) & PTE_UXN)) 10962306a36Sopenharmony_ci#define pte_cont(pte) (!!(pte_val(pte) & PTE_CONT)) 11062306a36Sopenharmony_ci#define pte_devmap(pte) (!!(pte_val(pte) & PTE_DEVMAP)) 11162306a36Sopenharmony_ci#define pte_tagged(pte) ((pte_val(pte) & PTE_ATTRINDX_MASK) == \ 11262306a36Sopenharmony_ci PTE_ATTRINDX(MT_NORMAL_TAGGED)) 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci#define pte_cont_addr_end(addr, end) \ 11562306a36Sopenharmony_ci({ unsigned long __boundary = ((addr) + CONT_PTE_SIZE) & CONT_PTE_MASK; \ 11662306a36Sopenharmony_ci (__boundary - 1 < (end) - 1) ? __boundary : (end); \ 11762306a36Sopenharmony_ci}) 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci#define pmd_cont_addr_end(addr, end) \ 12062306a36Sopenharmony_ci({ unsigned long __boundary = ((addr) + CONT_PMD_SIZE) & CONT_PMD_MASK; \ 12162306a36Sopenharmony_ci (__boundary - 1 < (end) - 1) ? __boundary : (end); \ 12262306a36Sopenharmony_ci}) 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci#define pte_hw_dirty(pte) (pte_write(pte) && !pte_rdonly(pte)) 12562306a36Sopenharmony_ci#define pte_sw_dirty(pte) (!!(pte_val(pte) & PTE_DIRTY)) 12662306a36Sopenharmony_ci#define pte_dirty(pte) (pte_sw_dirty(pte) || pte_hw_dirty(pte)) 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci#define pte_valid(pte) (!!(pte_val(pte) & PTE_VALID)) 12962306a36Sopenharmony_ci/* 13062306a36Sopenharmony_ci * Execute-only user mappings do not have the PTE_USER bit set. All valid 13162306a36Sopenharmony_ci * kernel mappings have the PTE_UXN bit set. 13262306a36Sopenharmony_ci */ 13362306a36Sopenharmony_ci#define pte_valid_not_user(pte) \ 13462306a36Sopenharmony_ci ((pte_val(pte) & (PTE_VALID | PTE_USER | PTE_UXN)) == (PTE_VALID | PTE_UXN)) 13562306a36Sopenharmony_ci/* 13662306a36Sopenharmony_ci * Could the pte be present in the TLB? We must check mm_tlb_flush_pending 13762306a36Sopenharmony_ci * so that we don't erroneously return false for pages that have been 13862306a36Sopenharmony_ci * remapped as PROT_NONE but are yet to be flushed from the TLB. 13962306a36Sopenharmony_ci * Note that we can't make any assumptions based on the state of the access 14062306a36Sopenharmony_ci * flag, since ptep_clear_flush_young() elides a DSB when invalidating the 14162306a36Sopenharmony_ci * TLB. 14262306a36Sopenharmony_ci */ 14362306a36Sopenharmony_ci#define pte_accessible(mm, pte) \ 14462306a36Sopenharmony_ci (mm_tlb_flush_pending(mm) ? pte_present(pte) : pte_valid(pte)) 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci/* 14762306a36Sopenharmony_ci * p??_access_permitted() is true for valid user mappings (PTE_USER 14862306a36Sopenharmony_ci * bit set, subject to the write permission check). For execute-only 14962306a36Sopenharmony_ci * mappings, like PROT_EXEC with EPAN (both PTE_USER and PTE_UXN bits 15062306a36Sopenharmony_ci * not set) must return false. PROT_NONE mappings do not have the 15162306a36Sopenharmony_ci * PTE_VALID bit set. 15262306a36Sopenharmony_ci */ 15362306a36Sopenharmony_ci#define pte_access_permitted(pte, write) \ 15462306a36Sopenharmony_ci (((pte_val(pte) & (PTE_VALID | PTE_USER)) == (PTE_VALID | PTE_USER)) && (!(write) || pte_write(pte))) 15562306a36Sopenharmony_ci#define pmd_access_permitted(pmd, write) \ 15662306a36Sopenharmony_ci (pte_access_permitted(pmd_pte(pmd), (write))) 15762306a36Sopenharmony_ci#define pud_access_permitted(pud, write) \ 15862306a36Sopenharmony_ci (pte_access_permitted(pud_pte(pud), (write))) 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_cistatic inline pte_t clear_pte_bit(pte_t pte, pgprot_t prot) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci pte_val(pte) &= ~pgprot_val(prot); 16362306a36Sopenharmony_ci return pte; 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cistatic inline pte_t set_pte_bit(pte_t pte, pgprot_t prot) 16762306a36Sopenharmony_ci{ 16862306a36Sopenharmony_ci pte_val(pte) |= pgprot_val(prot); 16962306a36Sopenharmony_ci return pte; 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_cistatic inline pmd_t clear_pmd_bit(pmd_t pmd, pgprot_t prot) 17362306a36Sopenharmony_ci{ 17462306a36Sopenharmony_ci pmd_val(pmd) &= ~pgprot_val(prot); 17562306a36Sopenharmony_ci return pmd; 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_cistatic inline pmd_t set_pmd_bit(pmd_t pmd, pgprot_t prot) 17962306a36Sopenharmony_ci{ 18062306a36Sopenharmony_ci pmd_val(pmd) |= pgprot_val(prot); 18162306a36Sopenharmony_ci return pmd; 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_cistatic inline pte_t pte_mkwrite_novma(pte_t pte) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci pte = set_pte_bit(pte, __pgprot(PTE_WRITE)); 18762306a36Sopenharmony_ci pte = clear_pte_bit(pte, __pgprot(PTE_RDONLY)); 18862306a36Sopenharmony_ci return pte; 18962306a36Sopenharmony_ci} 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_cistatic inline pte_t pte_mkclean(pte_t pte) 19262306a36Sopenharmony_ci{ 19362306a36Sopenharmony_ci pte = clear_pte_bit(pte, __pgprot(PTE_DIRTY)); 19462306a36Sopenharmony_ci pte = set_pte_bit(pte, __pgprot(PTE_RDONLY)); 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci return pte; 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_cistatic inline pte_t pte_mkdirty(pte_t pte) 20062306a36Sopenharmony_ci{ 20162306a36Sopenharmony_ci pte = set_pte_bit(pte, __pgprot(PTE_DIRTY)); 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci if (pte_write(pte)) 20462306a36Sopenharmony_ci pte = clear_pte_bit(pte, __pgprot(PTE_RDONLY)); 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci return pte; 20762306a36Sopenharmony_ci} 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_cistatic inline pte_t pte_wrprotect(pte_t pte) 21062306a36Sopenharmony_ci{ 21162306a36Sopenharmony_ci /* 21262306a36Sopenharmony_ci * If hardware-dirty (PTE_WRITE/DBM bit set and PTE_RDONLY 21362306a36Sopenharmony_ci * clear), set the PTE_DIRTY bit. 21462306a36Sopenharmony_ci */ 21562306a36Sopenharmony_ci if (pte_hw_dirty(pte)) 21662306a36Sopenharmony_ci pte = set_pte_bit(pte, __pgprot(PTE_DIRTY)); 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci pte = clear_pte_bit(pte, __pgprot(PTE_WRITE)); 21962306a36Sopenharmony_ci pte = set_pte_bit(pte, __pgprot(PTE_RDONLY)); 22062306a36Sopenharmony_ci return pte; 22162306a36Sopenharmony_ci} 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_cistatic inline pte_t pte_mkold(pte_t pte) 22462306a36Sopenharmony_ci{ 22562306a36Sopenharmony_ci return clear_pte_bit(pte, __pgprot(PTE_AF)); 22662306a36Sopenharmony_ci} 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_cistatic inline pte_t pte_mkyoung(pte_t pte) 22962306a36Sopenharmony_ci{ 23062306a36Sopenharmony_ci return set_pte_bit(pte, __pgprot(PTE_AF)); 23162306a36Sopenharmony_ci} 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_cistatic inline pte_t pte_mkspecial(pte_t pte) 23462306a36Sopenharmony_ci{ 23562306a36Sopenharmony_ci return set_pte_bit(pte, __pgprot(PTE_SPECIAL)); 23662306a36Sopenharmony_ci} 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_cistatic inline pte_t pte_mkcont(pte_t pte) 23962306a36Sopenharmony_ci{ 24062306a36Sopenharmony_ci pte = set_pte_bit(pte, __pgprot(PTE_CONT)); 24162306a36Sopenharmony_ci return set_pte_bit(pte, __pgprot(PTE_TYPE_PAGE)); 24262306a36Sopenharmony_ci} 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_cistatic inline pte_t pte_mknoncont(pte_t pte) 24562306a36Sopenharmony_ci{ 24662306a36Sopenharmony_ci return clear_pte_bit(pte, __pgprot(PTE_CONT)); 24762306a36Sopenharmony_ci} 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_cistatic inline pte_t pte_mkpresent(pte_t pte) 25062306a36Sopenharmony_ci{ 25162306a36Sopenharmony_ci return set_pte_bit(pte, __pgprot(PTE_VALID)); 25262306a36Sopenharmony_ci} 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_cistatic inline pmd_t pmd_mkcont(pmd_t pmd) 25562306a36Sopenharmony_ci{ 25662306a36Sopenharmony_ci return __pmd(pmd_val(pmd) | PMD_SECT_CONT); 25762306a36Sopenharmony_ci} 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_cistatic inline pte_t pte_mkdevmap(pte_t pte) 26062306a36Sopenharmony_ci{ 26162306a36Sopenharmony_ci return set_pte_bit(pte, __pgprot(PTE_DEVMAP | PTE_SPECIAL)); 26262306a36Sopenharmony_ci} 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_cistatic inline void set_pte(pte_t *ptep, pte_t pte) 26562306a36Sopenharmony_ci{ 26662306a36Sopenharmony_ci WRITE_ONCE(*ptep, pte); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci /* 26962306a36Sopenharmony_ci * Only if the new pte is valid and kernel, otherwise TLB maintenance 27062306a36Sopenharmony_ci * or update_mmu_cache() have the necessary barriers. 27162306a36Sopenharmony_ci */ 27262306a36Sopenharmony_ci if (pte_valid_not_user(pte)) { 27362306a36Sopenharmony_ci dsb(ishst); 27462306a36Sopenharmony_ci isb(); 27562306a36Sopenharmony_ci } 27662306a36Sopenharmony_ci} 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ciextern void __sync_icache_dcache(pte_t pteval); 27962306a36Sopenharmony_cibool pgattr_change_is_safe(u64 old, u64 new); 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci/* 28262306a36Sopenharmony_ci * PTE bits configuration in the presence of hardware Dirty Bit Management 28362306a36Sopenharmony_ci * (PTE_WRITE == PTE_DBM): 28462306a36Sopenharmony_ci * 28562306a36Sopenharmony_ci * Dirty Writable | PTE_RDONLY PTE_WRITE PTE_DIRTY (sw) 28662306a36Sopenharmony_ci * 0 0 | 1 0 0 28762306a36Sopenharmony_ci * 0 1 | 1 1 0 28862306a36Sopenharmony_ci * 1 0 | 1 0 1 28962306a36Sopenharmony_ci * 1 1 | 0 1 x 29062306a36Sopenharmony_ci * 29162306a36Sopenharmony_ci * When hardware DBM is not present, the sofware PTE_DIRTY bit is updated via 29262306a36Sopenharmony_ci * the page fault mechanism. Checking the dirty status of a pte becomes: 29362306a36Sopenharmony_ci * 29462306a36Sopenharmony_ci * PTE_DIRTY || (PTE_WRITE && !PTE_RDONLY) 29562306a36Sopenharmony_ci */ 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_cistatic inline void __check_safe_pte_update(struct mm_struct *mm, pte_t *ptep, 29862306a36Sopenharmony_ci pte_t pte) 29962306a36Sopenharmony_ci{ 30062306a36Sopenharmony_ci pte_t old_pte; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci if (!IS_ENABLED(CONFIG_DEBUG_VM)) 30362306a36Sopenharmony_ci return; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci old_pte = READ_ONCE(*ptep); 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci if (!pte_valid(old_pte) || !pte_valid(pte)) 30862306a36Sopenharmony_ci return; 30962306a36Sopenharmony_ci if (mm != current->active_mm && atomic_read(&mm->mm_users) <= 1) 31062306a36Sopenharmony_ci return; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci /* 31362306a36Sopenharmony_ci * Check for potential race with hardware updates of the pte 31462306a36Sopenharmony_ci * (ptep_set_access_flags safely changes valid ptes without going 31562306a36Sopenharmony_ci * through an invalid entry). 31662306a36Sopenharmony_ci */ 31762306a36Sopenharmony_ci VM_WARN_ONCE(!pte_young(pte), 31862306a36Sopenharmony_ci "%s: racy access flag clearing: 0x%016llx -> 0x%016llx", 31962306a36Sopenharmony_ci __func__, pte_val(old_pte), pte_val(pte)); 32062306a36Sopenharmony_ci VM_WARN_ONCE(pte_write(old_pte) && !pte_dirty(pte), 32162306a36Sopenharmony_ci "%s: racy dirty state clearing: 0x%016llx -> 0x%016llx", 32262306a36Sopenharmony_ci __func__, pte_val(old_pte), pte_val(pte)); 32362306a36Sopenharmony_ci VM_WARN_ONCE(!pgattr_change_is_safe(pte_val(old_pte), pte_val(pte)), 32462306a36Sopenharmony_ci "%s: unsafe attribute change: 0x%016llx -> 0x%016llx", 32562306a36Sopenharmony_ci __func__, pte_val(old_pte), pte_val(pte)); 32662306a36Sopenharmony_ci} 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_cistatic inline void __set_pte_at(struct mm_struct *mm, unsigned long addr, 32962306a36Sopenharmony_ci pte_t *ptep, pte_t pte) 33062306a36Sopenharmony_ci{ 33162306a36Sopenharmony_ci if (pte_present(pte) && pte_user_exec(pte) && !pte_special(pte)) 33262306a36Sopenharmony_ci __sync_icache_dcache(pte); 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci /* 33562306a36Sopenharmony_ci * If the PTE would provide user space access to the tags associated 33662306a36Sopenharmony_ci * with it then ensure that the MTE tags are synchronised. Although 33762306a36Sopenharmony_ci * pte_access_permitted() returns false for exec only mappings, they 33862306a36Sopenharmony_ci * don't expose tags (instruction fetches don't check tags). 33962306a36Sopenharmony_ci */ 34062306a36Sopenharmony_ci if (system_supports_mte() && pte_access_permitted(pte, false) && 34162306a36Sopenharmony_ci !pte_special(pte) && pte_tagged(pte)) 34262306a36Sopenharmony_ci mte_sync_tags(pte); 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci __check_safe_pte_update(mm, ptep, pte); 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci set_pte(ptep, pte); 34762306a36Sopenharmony_ci} 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_cistatic inline void set_ptes(struct mm_struct *mm, unsigned long addr, 35062306a36Sopenharmony_ci pte_t *ptep, pte_t pte, unsigned int nr) 35162306a36Sopenharmony_ci{ 35262306a36Sopenharmony_ci page_table_check_ptes_set(mm, ptep, pte, nr); 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci for (;;) { 35562306a36Sopenharmony_ci __set_pte_at(mm, addr, ptep, pte); 35662306a36Sopenharmony_ci if (--nr == 0) 35762306a36Sopenharmony_ci break; 35862306a36Sopenharmony_ci ptep++; 35962306a36Sopenharmony_ci addr += PAGE_SIZE; 36062306a36Sopenharmony_ci pte_val(pte) += PAGE_SIZE; 36162306a36Sopenharmony_ci } 36262306a36Sopenharmony_ci} 36362306a36Sopenharmony_ci#define set_ptes set_ptes 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci/* 36662306a36Sopenharmony_ci * Huge pte definitions. 36762306a36Sopenharmony_ci */ 36862306a36Sopenharmony_ci#define pte_mkhuge(pte) (__pte(pte_val(pte) & ~PTE_TABLE_BIT)) 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci/* 37162306a36Sopenharmony_ci * Hugetlb definitions. 37262306a36Sopenharmony_ci */ 37362306a36Sopenharmony_ci#define HUGE_MAX_HSTATE 4 37462306a36Sopenharmony_ci#define HPAGE_SHIFT PMD_SHIFT 37562306a36Sopenharmony_ci#define HPAGE_SIZE (_AC(1, UL) << HPAGE_SHIFT) 37662306a36Sopenharmony_ci#define HPAGE_MASK (~(HPAGE_SIZE - 1)) 37762306a36Sopenharmony_ci#define HUGETLB_PAGE_ORDER (HPAGE_SHIFT - PAGE_SHIFT) 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_cistatic inline pte_t pgd_pte(pgd_t pgd) 38062306a36Sopenharmony_ci{ 38162306a36Sopenharmony_ci return __pte(pgd_val(pgd)); 38262306a36Sopenharmony_ci} 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_cistatic inline pte_t p4d_pte(p4d_t p4d) 38562306a36Sopenharmony_ci{ 38662306a36Sopenharmony_ci return __pte(p4d_val(p4d)); 38762306a36Sopenharmony_ci} 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_cistatic inline pte_t pud_pte(pud_t pud) 39062306a36Sopenharmony_ci{ 39162306a36Sopenharmony_ci return __pte(pud_val(pud)); 39262306a36Sopenharmony_ci} 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_cistatic inline pud_t pte_pud(pte_t pte) 39562306a36Sopenharmony_ci{ 39662306a36Sopenharmony_ci return __pud(pte_val(pte)); 39762306a36Sopenharmony_ci} 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_cistatic inline pmd_t pud_pmd(pud_t pud) 40062306a36Sopenharmony_ci{ 40162306a36Sopenharmony_ci return __pmd(pud_val(pud)); 40262306a36Sopenharmony_ci} 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_cistatic inline pte_t pmd_pte(pmd_t pmd) 40562306a36Sopenharmony_ci{ 40662306a36Sopenharmony_ci return __pte(pmd_val(pmd)); 40762306a36Sopenharmony_ci} 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_cistatic inline pmd_t pte_pmd(pte_t pte) 41062306a36Sopenharmony_ci{ 41162306a36Sopenharmony_ci return __pmd(pte_val(pte)); 41262306a36Sopenharmony_ci} 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_cistatic inline pgprot_t mk_pud_sect_prot(pgprot_t prot) 41562306a36Sopenharmony_ci{ 41662306a36Sopenharmony_ci return __pgprot((pgprot_val(prot) & ~PUD_TABLE_BIT) | PUD_TYPE_SECT); 41762306a36Sopenharmony_ci} 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_cistatic inline pgprot_t mk_pmd_sect_prot(pgprot_t prot) 42062306a36Sopenharmony_ci{ 42162306a36Sopenharmony_ci return __pgprot((pgprot_val(prot) & ~PMD_TABLE_BIT) | PMD_TYPE_SECT); 42262306a36Sopenharmony_ci} 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_cistatic inline pte_t pte_swp_mkexclusive(pte_t pte) 42562306a36Sopenharmony_ci{ 42662306a36Sopenharmony_ci return set_pte_bit(pte, __pgprot(PTE_SWP_EXCLUSIVE)); 42762306a36Sopenharmony_ci} 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_cistatic inline int pte_swp_exclusive(pte_t pte) 43062306a36Sopenharmony_ci{ 43162306a36Sopenharmony_ci return pte_val(pte) & PTE_SWP_EXCLUSIVE; 43262306a36Sopenharmony_ci} 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_cistatic inline pte_t pte_swp_clear_exclusive(pte_t pte) 43562306a36Sopenharmony_ci{ 43662306a36Sopenharmony_ci return clear_pte_bit(pte, __pgprot(PTE_SWP_EXCLUSIVE)); 43762306a36Sopenharmony_ci} 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci/* 44062306a36Sopenharmony_ci * Select all bits except the pfn 44162306a36Sopenharmony_ci */ 44262306a36Sopenharmony_cistatic inline pgprot_t pte_pgprot(pte_t pte) 44362306a36Sopenharmony_ci{ 44462306a36Sopenharmony_ci unsigned long pfn = pte_pfn(pte); 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci return __pgprot(pte_val(pfn_pte(pfn, __pgprot(0))) ^ pte_val(pte)); 44762306a36Sopenharmony_ci} 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci#ifdef CONFIG_NUMA_BALANCING 45062306a36Sopenharmony_ci/* 45162306a36Sopenharmony_ci * See the comment in include/linux/pgtable.h 45262306a36Sopenharmony_ci */ 45362306a36Sopenharmony_cistatic inline int pte_protnone(pte_t pte) 45462306a36Sopenharmony_ci{ 45562306a36Sopenharmony_ci return (pte_val(pte) & (PTE_VALID | PTE_PROT_NONE)) == PTE_PROT_NONE; 45662306a36Sopenharmony_ci} 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_cistatic inline int pmd_protnone(pmd_t pmd) 45962306a36Sopenharmony_ci{ 46062306a36Sopenharmony_ci return pte_protnone(pmd_pte(pmd)); 46162306a36Sopenharmony_ci} 46262306a36Sopenharmony_ci#endif 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci#define pmd_present_invalid(pmd) (!!(pmd_val(pmd) & PMD_PRESENT_INVALID)) 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_cistatic inline int pmd_present(pmd_t pmd) 46762306a36Sopenharmony_ci{ 46862306a36Sopenharmony_ci return pte_present(pmd_pte(pmd)) || pmd_present_invalid(pmd); 46962306a36Sopenharmony_ci} 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci/* 47262306a36Sopenharmony_ci * THP definitions. 47362306a36Sopenharmony_ci */ 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci#ifdef CONFIG_TRANSPARENT_HUGEPAGE 47662306a36Sopenharmony_cistatic inline int pmd_trans_huge(pmd_t pmd) 47762306a36Sopenharmony_ci{ 47862306a36Sopenharmony_ci return pmd_val(pmd) && pmd_present(pmd) && !(pmd_val(pmd) & PMD_TABLE_BIT); 47962306a36Sopenharmony_ci} 48062306a36Sopenharmony_ci#endif /* CONFIG_TRANSPARENT_HUGEPAGE */ 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci#define pmd_dirty(pmd) pte_dirty(pmd_pte(pmd)) 48362306a36Sopenharmony_ci#define pmd_young(pmd) pte_young(pmd_pte(pmd)) 48462306a36Sopenharmony_ci#define pmd_valid(pmd) pte_valid(pmd_pte(pmd)) 48562306a36Sopenharmony_ci#define pmd_user(pmd) pte_user(pmd_pte(pmd)) 48662306a36Sopenharmony_ci#define pmd_user_exec(pmd) pte_user_exec(pmd_pte(pmd)) 48762306a36Sopenharmony_ci#define pmd_cont(pmd) pte_cont(pmd_pte(pmd)) 48862306a36Sopenharmony_ci#define pmd_wrprotect(pmd) pte_pmd(pte_wrprotect(pmd_pte(pmd))) 48962306a36Sopenharmony_ci#define pmd_mkold(pmd) pte_pmd(pte_mkold(pmd_pte(pmd))) 49062306a36Sopenharmony_ci#define pmd_mkwrite_novma(pmd) pte_pmd(pte_mkwrite_novma(pmd_pte(pmd))) 49162306a36Sopenharmony_ci#define pmd_mkclean(pmd) pte_pmd(pte_mkclean(pmd_pte(pmd))) 49262306a36Sopenharmony_ci#define pmd_mkdirty(pmd) pte_pmd(pte_mkdirty(pmd_pte(pmd))) 49362306a36Sopenharmony_ci#define pmd_mkyoung(pmd) pte_pmd(pte_mkyoung(pmd_pte(pmd))) 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_cistatic inline pmd_t pmd_mkinvalid(pmd_t pmd) 49662306a36Sopenharmony_ci{ 49762306a36Sopenharmony_ci pmd = set_pmd_bit(pmd, __pgprot(PMD_PRESENT_INVALID)); 49862306a36Sopenharmony_ci pmd = clear_pmd_bit(pmd, __pgprot(PMD_SECT_VALID)); 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci return pmd; 50162306a36Sopenharmony_ci} 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci#define pmd_thp_or_huge(pmd) (pmd_huge(pmd) || pmd_trans_huge(pmd)) 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci#define pmd_write(pmd) pte_write(pmd_pte(pmd)) 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci#define pmd_mkhuge(pmd) (__pmd(pmd_val(pmd) & ~PMD_TABLE_BIT)) 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci#ifdef CONFIG_TRANSPARENT_HUGEPAGE 51062306a36Sopenharmony_ci#define pmd_devmap(pmd) pte_devmap(pmd_pte(pmd)) 51162306a36Sopenharmony_ci#endif 51262306a36Sopenharmony_cistatic inline pmd_t pmd_mkdevmap(pmd_t pmd) 51362306a36Sopenharmony_ci{ 51462306a36Sopenharmony_ci return pte_pmd(set_pte_bit(pmd_pte(pmd), __pgprot(PTE_DEVMAP))); 51562306a36Sopenharmony_ci} 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci#define __pmd_to_phys(pmd) __pte_to_phys(pmd_pte(pmd)) 51862306a36Sopenharmony_ci#define __phys_to_pmd_val(phys) __phys_to_pte_val(phys) 51962306a36Sopenharmony_ci#define pmd_pfn(pmd) ((__pmd_to_phys(pmd) & PMD_MASK) >> PAGE_SHIFT) 52062306a36Sopenharmony_ci#define pfn_pmd(pfn,prot) __pmd(__phys_to_pmd_val((phys_addr_t)(pfn) << PAGE_SHIFT) | pgprot_val(prot)) 52162306a36Sopenharmony_ci#define mk_pmd(page,prot) pfn_pmd(page_to_pfn(page),prot) 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci#define pud_young(pud) pte_young(pud_pte(pud)) 52462306a36Sopenharmony_ci#define pud_mkyoung(pud) pte_pud(pte_mkyoung(pud_pte(pud))) 52562306a36Sopenharmony_ci#define pud_write(pud) pte_write(pud_pte(pud)) 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci#define pud_mkhuge(pud) (__pud(pud_val(pud) & ~PUD_TABLE_BIT)) 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci#define __pud_to_phys(pud) __pte_to_phys(pud_pte(pud)) 53062306a36Sopenharmony_ci#define __phys_to_pud_val(phys) __phys_to_pte_val(phys) 53162306a36Sopenharmony_ci#define pud_pfn(pud) ((__pud_to_phys(pud) & PUD_MASK) >> PAGE_SHIFT) 53262306a36Sopenharmony_ci#define pfn_pud(pfn,prot) __pud(__phys_to_pud_val((phys_addr_t)(pfn) << PAGE_SHIFT) | pgprot_val(prot)) 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_cistatic inline void set_pmd_at(struct mm_struct *mm, unsigned long addr, 53562306a36Sopenharmony_ci pmd_t *pmdp, pmd_t pmd) 53662306a36Sopenharmony_ci{ 53762306a36Sopenharmony_ci page_table_check_pmd_set(mm, pmdp, pmd); 53862306a36Sopenharmony_ci return __set_pte_at(mm, addr, (pte_t *)pmdp, pmd_pte(pmd)); 53962306a36Sopenharmony_ci} 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_cistatic inline void set_pud_at(struct mm_struct *mm, unsigned long addr, 54262306a36Sopenharmony_ci pud_t *pudp, pud_t pud) 54362306a36Sopenharmony_ci{ 54462306a36Sopenharmony_ci page_table_check_pud_set(mm, pudp, pud); 54562306a36Sopenharmony_ci return __set_pte_at(mm, addr, (pte_t *)pudp, pud_pte(pud)); 54662306a36Sopenharmony_ci} 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci#define __p4d_to_phys(p4d) __pte_to_phys(p4d_pte(p4d)) 54962306a36Sopenharmony_ci#define __phys_to_p4d_val(phys) __phys_to_pte_val(phys) 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci#define __pgd_to_phys(pgd) __pte_to_phys(pgd_pte(pgd)) 55262306a36Sopenharmony_ci#define __phys_to_pgd_val(phys) __phys_to_pte_val(phys) 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci#define __pgprot_modify(prot,mask,bits) \ 55562306a36Sopenharmony_ci __pgprot((pgprot_val(prot) & ~(mask)) | (bits)) 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci#define pgprot_nx(prot) \ 55862306a36Sopenharmony_ci __pgprot_modify(prot, PTE_MAYBE_GP, PTE_PXN) 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci/* 56162306a36Sopenharmony_ci * Mark the prot value as uncacheable and unbufferable. 56262306a36Sopenharmony_ci */ 56362306a36Sopenharmony_ci#define pgprot_noncached(prot) \ 56462306a36Sopenharmony_ci __pgprot_modify(prot, PTE_ATTRINDX_MASK, PTE_ATTRINDX(MT_DEVICE_nGnRnE) | PTE_PXN | PTE_UXN) 56562306a36Sopenharmony_ci#define pgprot_writecombine(prot) \ 56662306a36Sopenharmony_ci __pgprot_modify(prot, PTE_ATTRINDX_MASK, PTE_ATTRINDX(MT_NORMAL_NC) | PTE_PXN | PTE_UXN) 56762306a36Sopenharmony_ci#define pgprot_device(prot) \ 56862306a36Sopenharmony_ci __pgprot_modify(prot, PTE_ATTRINDX_MASK, PTE_ATTRINDX(MT_DEVICE_nGnRE) | PTE_PXN | PTE_UXN) 56962306a36Sopenharmony_ci#define pgprot_tagged(prot) \ 57062306a36Sopenharmony_ci __pgprot_modify(prot, PTE_ATTRINDX_MASK, PTE_ATTRINDX(MT_NORMAL_TAGGED)) 57162306a36Sopenharmony_ci#define pgprot_mhp pgprot_tagged 57262306a36Sopenharmony_ci/* 57362306a36Sopenharmony_ci * DMA allocations for non-coherent devices use what the Arm architecture calls 57462306a36Sopenharmony_ci * "Normal non-cacheable" memory, which permits speculation, unaligned accesses 57562306a36Sopenharmony_ci * and merging of writes. This is different from "Device-nGnR[nE]" memory which 57662306a36Sopenharmony_ci * is intended for MMIO and thus forbids speculation, preserves access size, 57762306a36Sopenharmony_ci * requires strict alignment and can also force write responses to come from the 57862306a36Sopenharmony_ci * endpoint. 57962306a36Sopenharmony_ci */ 58062306a36Sopenharmony_ci#define pgprot_dmacoherent(prot) \ 58162306a36Sopenharmony_ci __pgprot_modify(prot, PTE_ATTRINDX_MASK, \ 58262306a36Sopenharmony_ci PTE_ATTRINDX(MT_NORMAL_NC) | PTE_PXN | PTE_UXN) 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci#define __HAVE_PHYS_MEM_ACCESS_PROT 58562306a36Sopenharmony_cistruct file; 58662306a36Sopenharmony_ciextern pgprot_t phys_mem_access_prot(struct file *file, unsigned long pfn, 58762306a36Sopenharmony_ci unsigned long size, pgprot_t vma_prot); 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci#define pmd_none(pmd) (!pmd_val(pmd)) 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci#define pmd_table(pmd) ((pmd_val(pmd) & PMD_TYPE_MASK) == \ 59262306a36Sopenharmony_ci PMD_TYPE_TABLE) 59362306a36Sopenharmony_ci#define pmd_sect(pmd) ((pmd_val(pmd) & PMD_TYPE_MASK) == \ 59462306a36Sopenharmony_ci PMD_TYPE_SECT) 59562306a36Sopenharmony_ci#define pmd_leaf(pmd) (pmd_present(pmd) && !pmd_table(pmd)) 59662306a36Sopenharmony_ci#define pmd_bad(pmd) (!pmd_table(pmd)) 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci#define pmd_leaf_size(pmd) (pmd_cont(pmd) ? CONT_PMD_SIZE : PMD_SIZE) 59962306a36Sopenharmony_ci#define pte_leaf_size(pte) (pte_cont(pte) ? CONT_PTE_SIZE : PAGE_SIZE) 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci#if defined(CONFIG_ARM64_64K_PAGES) || CONFIG_PGTABLE_LEVELS < 3 60262306a36Sopenharmony_cistatic inline bool pud_sect(pud_t pud) { return false; } 60362306a36Sopenharmony_cistatic inline bool pud_table(pud_t pud) { return true; } 60462306a36Sopenharmony_ci#else 60562306a36Sopenharmony_ci#define pud_sect(pud) ((pud_val(pud) & PUD_TYPE_MASK) == \ 60662306a36Sopenharmony_ci PUD_TYPE_SECT) 60762306a36Sopenharmony_ci#define pud_table(pud) ((pud_val(pud) & PUD_TYPE_MASK) == \ 60862306a36Sopenharmony_ci PUD_TYPE_TABLE) 60962306a36Sopenharmony_ci#endif 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ciextern pgd_t init_pg_dir[PTRS_PER_PGD]; 61262306a36Sopenharmony_ciextern pgd_t init_pg_end[]; 61362306a36Sopenharmony_ciextern pgd_t swapper_pg_dir[PTRS_PER_PGD]; 61462306a36Sopenharmony_ciextern pgd_t idmap_pg_dir[PTRS_PER_PGD]; 61562306a36Sopenharmony_ciextern pgd_t tramp_pg_dir[PTRS_PER_PGD]; 61662306a36Sopenharmony_ciextern pgd_t reserved_pg_dir[PTRS_PER_PGD]; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ciextern void set_swapper_pgd(pgd_t *pgdp, pgd_t pgd); 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_cistatic inline bool in_swapper_pgdir(void *addr) 62162306a36Sopenharmony_ci{ 62262306a36Sopenharmony_ci return ((unsigned long)addr & PAGE_MASK) == 62362306a36Sopenharmony_ci ((unsigned long)swapper_pg_dir & PAGE_MASK); 62462306a36Sopenharmony_ci} 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_cistatic inline void set_pmd(pmd_t *pmdp, pmd_t pmd) 62762306a36Sopenharmony_ci{ 62862306a36Sopenharmony_ci#ifdef __PAGETABLE_PMD_FOLDED 62962306a36Sopenharmony_ci if (in_swapper_pgdir(pmdp)) { 63062306a36Sopenharmony_ci set_swapper_pgd((pgd_t *)pmdp, __pgd(pmd_val(pmd))); 63162306a36Sopenharmony_ci return; 63262306a36Sopenharmony_ci } 63362306a36Sopenharmony_ci#endif /* __PAGETABLE_PMD_FOLDED */ 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci WRITE_ONCE(*pmdp, pmd); 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci if (pmd_valid(pmd)) { 63862306a36Sopenharmony_ci dsb(ishst); 63962306a36Sopenharmony_ci isb(); 64062306a36Sopenharmony_ci } 64162306a36Sopenharmony_ci} 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_cistatic inline void pmd_clear(pmd_t *pmdp) 64462306a36Sopenharmony_ci{ 64562306a36Sopenharmony_ci set_pmd(pmdp, __pmd(0)); 64662306a36Sopenharmony_ci} 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_cistatic inline phys_addr_t pmd_page_paddr(pmd_t pmd) 64962306a36Sopenharmony_ci{ 65062306a36Sopenharmony_ci return __pmd_to_phys(pmd); 65162306a36Sopenharmony_ci} 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_cistatic inline unsigned long pmd_page_vaddr(pmd_t pmd) 65462306a36Sopenharmony_ci{ 65562306a36Sopenharmony_ci return (unsigned long)__va(pmd_page_paddr(pmd)); 65662306a36Sopenharmony_ci} 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci/* Find an entry in the third-level page table. */ 65962306a36Sopenharmony_ci#define pte_offset_phys(dir,addr) (pmd_page_paddr(READ_ONCE(*(dir))) + pte_index(addr) * sizeof(pte_t)) 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci#define pte_set_fixmap(addr) ((pte_t *)set_fixmap_offset(FIX_PTE, addr)) 66262306a36Sopenharmony_ci#define pte_set_fixmap_offset(pmd, addr) pte_set_fixmap(pte_offset_phys(pmd, addr)) 66362306a36Sopenharmony_ci#define pte_clear_fixmap() clear_fixmap(FIX_PTE) 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci#define pmd_page(pmd) phys_to_page(__pmd_to_phys(pmd)) 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci/* use ONLY for statically allocated translation tables */ 66862306a36Sopenharmony_ci#define pte_offset_kimg(dir,addr) ((pte_t *)__phys_to_kimg(pte_offset_phys((dir), (addr)))) 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci/* 67162306a36Sopenharmony_ci * Conversion functions: convert a page and protection to a page entry, 67262306a36Sopenharmony_ci * and a page entry and page directory to the page they refer to. 67362306a36Sopenharmony_ci */ 67462306a36Sopenharmony_ci#define mk_pte(page,prot) pfn_pte(page_to_pfn(page),prot) 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci#if CONFIG_PGTABLE_LEVELS > 2 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci#define pmd_ERROR(e) \ 67962306a36Sopenharmony_ci pr_err("%s:%d: bad pmd %016llx.\n", __FILE__, __LINE__, pmd_val(e)) 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci#define pud_none(pud) (!pud_val(pud)) 68262306a36Sopenharmony_ci#define pud_bad(pud) (!pud_table(pud)) 68362306a36Sopenharmony_ci#define pud_present(pud) pte_present(pud_pte(pud)) 68462306a36Sopenharmony_ci#define pud_leaf(pud) (pud_present(pud) && !pud_table(pud)) 68562306a36Sopenharmony_ci#define pud_valid(pud) pte_valid(pud_pte(pud)) 68662306a36Sopenharmony_ci#define pud_user(pud) pte_user(pud_pte(pud)) 68762306a36Sopenharmony_ci#define pud_user_exec(pud) pte_user_exec(pud_pte(pud)) 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_cistatic inline void set_pud(pud_t *pudp, pud_t pud) 69062306a36Sopenharmony_ci{ 69162306a36Sopenharmony_ci#ifdef __PAGETABLE_PUD_FOLDED 69262306a36Sopenharmony_ci if (in_swapper_pgdir(pudp)) { 69362306a36Sopenharmony_ci set_swapper_pgd((pgd_t *)pudp, __pgd(pud_val(pud))); 69462306a36Sopenharmony_ci return; 69562306a36Sopenharmony_ci } 69662306a36Sopenharmony_ci#endif /* __PAGETABLE_PUD_FOLDED */ 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci WRITE_ONCE(*pudp, pud); 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci if (pud_valid(pud)) { 70162306a36Sopenharmony_ci dsb(ishst); 70262306a36Sopenharmony_ci isb(); 70362306a36Sopenharmony_ci } 70462306a36Sopenharmony_ci} 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_cistatic inline void pud_clear(pud_t *pudp) 70762306a36Sopenharmony_ci{ 70862306a36Sopenharmony_ci set_pud(pudp, __pud(0)); 70962306a36Sopenharmony_ci} 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_cistatic inline phys_addr_t pud_page_paddr(pud_t pud) 71262306a36Sopenharmony_ci{ 71362306a36Sopenharmony_ci return __pud_to_phys(pud); 71462306a36Sopenharmony_ci} 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_cistatic inline pmd_t *pud_pgtable(pud_t pud) 71762306a36Sopenharmony_ci{ 71862306a36Sopenharmony_ci return (pmd_t *)__va(pud_page_paddr(pud)); 71962306a36Sopenharmony_ci} 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci/* Find an entry in the second-level page table. */ 72262306a36Sopenharmony_ci#define pmd_offset_phys(dir, addr) (pud_page_paddr(READ_ONCE(*(dir))) + pmd_index(addr) * sizeof(pmd_t)) 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci#define pmd_set_fixmap(addr) ((pmd_t *)set_fixmap_offset(FIX_PMD, addr)) 72562306a36Sopenharmony_ci#define pmd_set_fixmap_offset(pud, addr) pmd_set_fixmap(pmd_offset_phys(pud, addr)) 72662306a36Sopenharmony_ci#define pmd_clear_fixmap() clear_fixmap(FIX_PMD) 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci#define pud_page(pud) phys_to_page(__pud_to_phys(pud)) 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci/* use ONLY for statically allocated translation tables */ 73162306a36Sopenharmony_ci#define pmd_offset_kimg(dir,addr) ((pmd_t *)__phys_to_kimg(pmd_offset_phys((dir), (addr)))) 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci#else 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci#define pud_page_paddr(pud) ({ BUILD_BUG(); 0; }) 73662306a36Sopenharmony_ci#define pud_user_exec(pud) pud_user(pud) /* Always 0 with folding */ 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci/* Match pmd_offset folding in <asm/generic/pgtable-nopmd.h> */ 73962306a36Sopenharmony_ci#define pmd_set_fixmap(addr) NULL 74062306a36Sopenharmony_ci#define pmd_set_fixmap_offset(pudp, addr) ((pmd_t *)pudp) 74162306a36Sopenharmony_ci#define pmd_clear_fixmap() 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci#define pmd_offset_kimg(dir,addr) ((pmd_t *)dir) 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci#endif /* CONFIG_PGTABLE_LEVELS > 2 */ 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci#if CONFIG_PGTABLE_LEVELS > 3 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci#define pud_ERROR(e) \ 75062306a36Sopenharmony_ci pr_err("%s:%d: bad pud %016llx.\n", __FILE__, __LINE__, pud_val(e)) 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci#define p4d_none(p4d) (!p4d_val(p4d)) 75362306a36Sopenharmony_ci#define p4d_bad(p4d) (!(p4d_val(p4d) & 2)) 75462306a36Sopenharmony_ci#define p4d_present(p4d) (p4d_val(p4d)) 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_cistatic inline void set_p4d(p4d_t *p4dp, p4d_t p4d) 75762306a36Sopenharmony_ci{ 75862306a36Sopenharmony_ci if (in_swapper_pgdir(p4dp)) { 75962306a36Sopenharmony_ci set_swapper_pgd((pgd_t *)p4dp, __pgd(p4d_val(p4d))); 76062306a36Sopenharmony_ci return; 76162306a36Sopenharmony_ci } 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci WRITE_ONCE(*p4dp, p4d); 76462306a36Sopenharmony_ci dsb(ishst); 76562306a36Sopenharmony_ci isb(); 76662306a36Sopenharmony_ci} 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_cistatic inline void p4d_clear(p4d_t *p4dp) 76962306a36Sopenharmony_ci{ 77062306a36Sopenharmony_ci set_p4d(p4dp, __p4d(0)); 77162306a36Sopenharmony_ci} 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_cistatic inline phys_addr_t p4d_page_paddr(p4d_t p4d) 77462306a36Sopenharmony_ci{ 77562306a36Sopenharmony_ci return __p4d_to_phys(p4d); 77662306a36Sopenharmony_ci} 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_cistatic inline pud_t *p4d_pgtable(p4d_t p4d) 77962306a36Sopenharmony_ci{ 78062306a36Sopenharmony_ci return (pud_t *)__va(p4d_page_paddr(p4d)); 78162306a36Sopenharmony_ci} 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci/* Find an entry in the first-level page table. */ 78462306a36Sopenharmony_ci#define pud_offset_phys(dir, addr) (p4d_page_paddr(READ_ONCE(*(dir))) + pud_index(addr) * sizeof(pud_t)) 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci#define pud_set_fixmap(addr) ((pud_t *)set_fixmap_offset(FIX_PUD, addr)) 78762306a36Sopenharmony_ci#define pud_set_fixmap_offset(p4d, addr) pud_set_fixmap(pud_offset_phys(p4d, addr)) 78862306a36Sopenharmony_ci#define pud_clear_fixmap() clear_fixmap(FIX_PUD) 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci#define p4d_page(p4d) pfn_to_page(__phys_to_pfn(__p4d_to_phys(p4d))) 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci/* use ONLY for statically allocated translation tables */ 79362306a36Sopenharmony_ci#define pud_offset_kimg(dir,addr) ((pud_t *)__phys_to_kimg(pud_offset_phys((dir), (addr)))) 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci#else 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci#define p4d_page_paddr(p4d) ({ BUILD_BUG(); 0;}) 79862306a36Sopenharmony_ci#define pgd_page_paddr(pgd) ({ BUILD_BUG(); 0;}) 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci/* Match pud_offset folding in <asm/generic/pgtable-nopud.h> */ 80162306a36Sopenharmony_ci#define pud_set_fixmap(addr) NULL 80262306a36Sopenharmony_ci#define pud_set_fixmap_offset(pgdp, addr) ((pud_t *)pgdp) 80362306a36Sopenharmony_ci#define pud_clear_fixmap() 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci#define pud_offset_kimg(dir,addr) ((pud_t *)dir) 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci#endif /* CONFIG_PGTABLE_LEVELS > 3 */ 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci#define pgd_ERROR(e) \ 81062306a36Sopenharmony_ci pr_err("%s:%d: bad pgd %016llx.\n", __FILE__, __LINE__, pgd_val(e)) 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci#define pgd_set_fixmap(addr) ((pgd_t *)set_fixmap_offset(FIX_PGD, addr)) 81362306a36Sopenharmony_ci#define pgd_clear_fixmap() clear_fixmap(FIX_PGD) 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_cistatic inline pte_t pte_modify(pte_t pte, pgprot_t newprot) 81662306a36Sopenharmony_ci{ 81762306a36Sopenharmony_ci /* 81862306a36Sopenharmony_ci * Normal and Normal-Tagged are two different memory types and indices 81962306a36Sopenharmony_ci * in MAIR_EL1. The mask below has to include PTE_ATTRINDX_MASK. 82062306a36Sopenharmony_ci */ 82162306a36Sopenharmony_ci const pteval_t mask = PTE_USER | PTE_PXN | PTE_UXN | PTE_RDONLY | 82262306a36Sopenharmony_ci PTE_PROT_NONE | PTE_VALID | PTE_WRITE | PTE_GP | 82362306a36Sopenharmony_ci PTE_ATTRINDX_MASK; 82462306a36Sopenharmony_ci /* preserve the hardware dirty information */ 82562306a36Sopenharmony_ci if (pte_hw_dirty(pte)) 82662306a36Sopenharmony_ci pte = set_pte_bit(pte, __pgprot(PTE_DIRTY)); 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci pte_val(pte) = (pte_val(pte) & ~mask) | (pgprot_val(newprot) & mask); 82962306a36Sopenharmony_ci /* 83062306a36Sopenharmony_ci * If we end up clearing hw dirtiness for a sw-dirty PTE, set hardware 83162306a36Sopenharmony_ci * dirtiness again. 83262306a36Sopenharmony_ci */ 83362306a36Sopenharmony_ci if (pte_sw_dirty(pte)) 83462306a36Sopenharmony_ci pte = pte_mkdirty(pte); 83562306a36Sopenharmony_ci return pte; 83662306a36Sopenharmony_ci} 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_cistatic inline pmd_t pmd_modify(pmd_t pmd, pgprot_t newprot) 83962306a36Sopenharmony_ci{ 84062306a36Sopenharmony_ci return pte_pmd(pte_modify(pmd_pte(pmd), newprot)); 84162306a36Sopenharmony_ci} 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci#define __HAVE_ARCH_PTEP_SET_ACCESS_FLAGS 84462306a36Sopenharmony_ciextern int ptep_set_access_flags(struct vm_area_struct *vma, 84562306a36Sopenharmony_ci unsigned long address, pte_t *ptep, 84662306a36Sopenharmony_ci pte_t entry, int dirty); 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci#ifdef CONFIG_TRANSPARENT_HUGEPAGE 84962306a36Sopenharmony_ci#define __HAVE_ARCH_PMDP_SET_ACCESS_FLAGS 85062306a36Sopenharmony_cistatic inline int pmdp_set_access_flags(struct vm_area_struct *vma, 85162306a36Sopenharmony_ci unsigned long address, pmd_t *pmdp, 85262306a36Sopenharmony_ci pmd_t entry, int dirty) 85362306a36Sopenharmony_ci{ 85462306a36Sopenharmony_ci return ptep_set_access_flags(vma, address, (pte_t *)pmdp, pmd_pte(entry), dirty); 85562306a36Sopenharmony_ci} 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_cistatic inline int pud_devmap(pud_t pud) 85862306a36Sopenharmony_ci{ 85962306a36Sopenharmony_ci return 0; 86062306a36Sopenharmony_ci} 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_cistatic inline int pgd_devmap(pgd_t pgd) 86362306a36Sopenharmony_ci{ 86462306a36Sopenharmony_ci return 0; 86562306a36Sopenharmony_ci} 86662306a36Sopenharmony_ci#endif 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci#ifdef CONFIG_PAGE_TABLE_CHECK 86962306a36Sopenharmony_cistatic inline bool pte_user_accessible_page(pte_t pte) 87062306a36Sopenharmony_ci{ 87162306a36Sopenharmony_ci return pte_present(pte) && (pte_user(pte) || pte_user_exec(pte)); 87262306a36Sopenharmony_ci} 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_cistatic inline bool pmd_user_accessible_page(pmd_t pmd) 87562306a36Sopenharmony_ci{ 87662306a36Sopenharmony_ci return pmd_leaf(pmd) && !pmd_present_invalid(pmd) && (pmd_user(pmd) || pmd_user_exec(pmd)); 87762306a36Sopenharmony_ci} 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_cistatic inline bool pud_user_accessible_page(pud_t pud) 88062306a36Sopenharmony_ci{ 88162306a36Sopenharmony_ci return pud_leaf(pud) && (pud_user(pud) || pud_user_exec(pud)); 88262306a36Sopenharmony_ci} 88362306a36Sopenharmony_ci#endif 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci/* 88662306a36Sopenharmony_ci * Atomic pte/pmd modifications. 88762306a36Sopenharmony_ci */ 88862306a36Sopenharmony_ci#define __HAVE_ARCH_PTEP_TEST_AND_CLEAR_YOUNG 88962306a36Sopenharmony_cistatic inline int __ptep_test_and_clear_young(pte_t *ptep) 89062306a36Sopenharmony_ci{ 89162306a36Sopenharmony_ci pte_t old_pte, pte; 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci pte = READ_ONCE(*ptep); 89462306a36Sopenharmony_ci do { 89562306a36Sopenharmony_ci old_pte = pte; 89662306a36Sopenharmony_ci pte = pte_mkold(pte); 89762306a36Sopenharmony_ci pte_val(pte) = cmpxchg_relaxed(&pte_val(*ptep), 89862306a36Sopenharmony_ci pte_val(old_pte), pte_val(pte)); 89962306a36Sopenharmony_ci } while (pte_val(pte) != pte_val(old_pte)); 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci return pte_young(pte); 90262306a36Sopenharmony_ci} 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_cistatic inline int ptep_test_and_clear_young(struct vm_area_struct *vma, 90562306a36Sopenharmony_ci unsigned long address, 90662306a36Sopenharmony_ci pte_t *ptep) 90762306a36Sopenharmony_ci{ 90862306a36Sopenharmony_ci return __ptep_test_and_clear_young(ptep); 90962306a36Sopenharmony_ci} 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci#define __HAVE_ARCH_PTEP_CLEAR_YOUNG_FLUSH 91262306a36Sopenharmony_cistatic inline int ptep_clear_flush_young(struct vm_area_struct *vma, 91362306a36Sopenharmony_ci unsigned long address, pte_t *ptep) 91462306a36Sopenharmony_ci{ 91562306a36Sopenharmony_ci int young = ptep_test_and_clear_young(vma, address, ptep); 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci if (young) { 91862306a36Sopenharmony_ci /* 91962306a36Sopenharmony_ci * We can elide the trailing DSB here since the worst that can 92062306a36Sopenharmony_ci * happen is that a CPU continues to use the young entry in its 92162306a36Sopenharmony_ci * TLB and we mistakenly reclaim the associated page. The 92262306a36Sopenharmony_ci * window for such an event is bounded by the next 92362306a36Sopenharmony_ci * context-switch, which provides a DSB to complete the TLB 92462306a36Sopenharmony_ci * invalidation. 92562306a36Sopenharmony_ci */ 92662306a36Sopenharmony_ci flush_tlb_page_nosync(vma, address); 92762306a36Sopenharmony_ci } 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci return young; 93062306a36Sopenharmony_ci} 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci#ifdef CONFIG_TRANSPARENT_HUGEPAGE 93362306a36Sopenharmony_ci#define __HAVE_ARCH_PMDP_TEST_AND_CLEAR_YOUNG 93462306a36Sopenharmony_cistatic inline int pmdp_test_and_clear_young(struct vm_area_struct *vma, 93562306a36Sopenharmony_ci unsigned long address, 93662306a36Sopenharmony_ci pmd_t *pmdp) 93762306a36Sopenharmony_ci{ 93862306a36Sopenharmony_ci return ptep_test_and_clear_young(vma, address, (pte_t *)pmdp); 93962306a36Sopenharmony_ci} 94062306a36Sopenharmony_ci#endif /* CONFIG_TRANSPARENT_HUGEPAGE */ 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_ci#define __HAVE_ARCH_PTEP_GET_AND_CLEAR 94362306a36Sopenharmony_cistatic inline pte_t ptep_get_and_clear(struct mm_struct *mm, 94462306a36Sopenharmony_ci unsigned long address, pte_t *ptep) 94562306a36Sopenharmony_ci{ 94662306a36Sopenharmony_ci pte_t pte = __pte(xchg_relaxed(&pte_val(*ptep), 0)); 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci page_table_check_pte_clear(mm, pte); 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_ci return pte; 95162306a36Sopenharmony_ci} 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci#ifdef CONFIG_TRANSPARENT_HUGEPAGE 95462306a36Sopenharmony_ci#define __HAVE_ARCH_PMDP_HUGE_GET_AND_CLEAR 95562306a36Sopenharmony_cistatic inline pmd_t pmdp_huge_get_and_clear(struct mm_struct *mm, 95662306a36Sopenharmony_ci unsigned long address, pmd_t *pmdp) 95762306a36Sopenharmony_ci{ 95862306a36Sopenharmony_ci pmd_t pmd = __pmd(xchg_relaxed(&pmd_val(*pmdp), 0)); 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ci page_table_check_pmd_clear(mm, pmd); 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci return pmd; 96362306a36Sopenharmony_ci} 96462306a36Sopenharmony_ci#endif /* CONFIG_TRANSPARENT_HUGEPAGE */ 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci/* 96762306a36Sopenharmony_ci * ptep_set_wrprotect - mark read-only while trasferring potential hardware 96862306a36Sopenharmony_ci * dirty status (PTE_DBM && !PTE_RDONLY) to the software PTE_DIRTY bit. 96962306a36Sopenharmony_ci */ 97062306a36Sopenharmony_ci#define __HAVE_ARCH_PTEP_SET_WRPROTECT 97162306a36Sopenharmony_cistatic inline void ptep_set_wrprotect(struct mm_struct *mm, unsigned long address, pte_t *ptep) 97262306a36Sopenharmony_ci{ 97362306a36Sopenharmony_ci pte_t old_pte, pte; 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci pte = READ_ONCE(*ptep); 97662306a36Sopenharmony_ci do { 97762306a36Sopenharmony_ci old_pte = pte; 97862306a36Sopenharmony_ci pte = pte_wrprotect(pte); 97962306a36Sopenharmony_ci pte_val(pte) = cmpxchg_relaxed(&pte_val(*ptep), 98062306a36Sopenharmony_ci pte_val(old_pte), pte_val(pte)); 98162306a36Sopenharmony_ci } while (pte_val(pte) != pte_val(old_pte)); 98262306a36Sopenharmony_ci} 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_ci#ifdef CONFIG_TRANSPARENT_HUGEPAGE 98562306a36Sopenharmony_ci#define __HAVE_ARCH_PMDP_SET_WRPROTECT 98662306a36Sopenharmony_cistatic inline void pmdp_set_wrprotect(struct mm_struct *mm, 98762306a36Sopenharmony_ci unsigned long address, pmd_t *pmdp) 98862306a36Sopenharmony_ci{ 98962306a36Sopenharmony_ci ptep_set_wrprotect(mm, address, (pte_t *)pmdp); 99062306a36Sopenharmony_ci} 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_ci#define pmdp_establish pmdp_establish 99362306a36Sopenharmony_cistatic inline pmd_t pmdp_establish(struct vm_area_struct *vma, 99462306a36Sopenharmony_ci unsigned long address, pmd_t *pmdp, pmd_t pmd) 99562306a36Sopenharmony_ci{ 99662306a36Sopenharmony_ci page_table_check_pmd_set(vma->vm_mm, pmdp, pmd); 99762306a36Sopenharmony_ci return __pmd(xchg_relaxed(&pmd_val(*pmdp), pmd_val(pmd))); 99862306a36Sopenharmony_ci} 99962306a36Sopenharmony_ci#endif 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci/* 100262306a36Sopenharmony_ci * Encode and decode a swap entry: 100362306a36Sopenharmony_ci * bits 0-1: present (must be zero) 100462306a36Sopenharmony_ci * bits 2: remember PG_anon_exclusive 100562306a36Sopenharmony_ci * bits 3-7: swap type 100662306a36Sopenharmony_ci * bits 8-57: swap offset 100762306a36Sopenharmony_ci * bit 58: PTE_PROT_NONE (must be zero) 100862306a36Sopenharmony_ci */ 100962306a36Sopenharmony_ci#define __SWP_TYPE_SHIFT 3 101062306a36Sopenharmony_ci#define __SWP_TYPE_BITS 5 101162306a36Sopenharmony_ci#define __SWP_OFFSET_BITS 50 101262306a36Sopenharmony_ci#define __SWP_TYPE_MASK ((1 << __SWP_TYPE_BITS) - 1) 101362306a36Sopenharmony_ci#define __SWP_OFFSET_SHIFT (__SWP_TYPE_BITS + __SWP_TYPE_SHIFT) 101462306a36Sopenharmony_ci#define __SWP_OFFSET_MASK ((1UL << __SWP_OFFSET_BITS) - 1) 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_ci#define __swp_type(x) (((x).val >> __SWP_TYPE_SHIFT) & __SWP_TYPE_MASK) 101762306a36Sopenharmony_ci#define __swp_offset(x) (((x).val >> __SWP_OFFSET_SHIFT) & __SWP_OFFSET_MASK) 101862306a36Sopenharmony_ci#define __swp_entry(type,offset) ((swp_entry_t) { ((type) << __SWP_TYPE_SHIFT) | ((offset) << __SWP_OFFSET_SHIFT) }) 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ci#define __pte_to_swp_entry(pte) ((swp_entry_t) { pte_val(pte) }) 102162306a36Sopenharmony_ci#define __swp_entry_to_pte(swp) ((pte_t) { (swp).val }) 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci#ifdef CONFIG_ARCH_ENABLE_THP_MIGRATION 102462306a36Sopenharmony_ci#define __pmd_to_swp_entry(pmd) ((swp_entry_t) { pmd_val(pmd) }) 102562306a36Sopenharmony_ci#define __swp_entry_to_pmd(swp) __pmd((swp).val) 102662306a36Sopenharmony_ci#endif /* CONFIG_ARCH_ENABLE_THP_MIGRATION */ 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ci/* 102962306a36Sopenharmony_ci * Ensure that there are not more swap files than can be encoded in the kernel 103062306a36Sopenharmony_ci * PTEs. 103162306a36Sopenharmony_ci */ 103262306a36Sopenharmony_ci#define MAX_SWAPFILES_CHECK() BUILD_BUG_ON(MAX_SWAPFILES_SHIFT > __SWP_TYPE_BITS) 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci#ifdef CONFIG_ARM64_MTE 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ci#define __HAVE_ARCH_PREPARE_TO_SWAP 103762306a36Sopenharmony_cistatic inline int arch_prepare_to_swap(struct page *page) 103862306a36Sopenharmony_ci{ 103962306a36Sopenharmony_ci if (system_supports_mte()) 104062306a36Sopenharmony_ci return mte_save_tags(page); 104162306a36Sopenharmony_ci return 0; 104262306a36Sopenharmony_ci} 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_ci#define __HAVE_ARCH_SWAP_INVALIDATE 104562306a36Sopenharmony_cistatic inline void arch_swap_invalidate_page(int type, pgoff_t offset) 104662306a36Sopenharmony_ci{ 104762306a36Sopenharmony_ci if (system_supports_mte()) 104862306a36Sopenharmony_ci mte_invalidate_tags(type, offset); 104962306a36Sopenharmony_ci} 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_cistatic inline void arch_swap_invalidate_area(int type) 105262306a36Sopenharmony_ci{ 105362306a36Sopenharmony_ci if (system_supports_mte()) 105462306a36Sopenharmony_ci mte_invalidate_tags_area(type); 105562306a36Sopenharmony_ci} 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_ci#define __HAVE_ARCH_SWAP_RESTORE 105862306a36Sopenharmony_cistatic inline void arch_swap_restore(swp_entry_t entry, struct folio *folio) 105962306a36Sopenharmony_ci{ 106062306a36Sopenharmony_ci if (system_supports_mte()) 106162306a36Sopenharmony_ci mte_restore_tags(entry, &folio->page); 106262306a36Sopenharmony_ci} 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci#endif /* CONFIG_ARM64_MTE */ 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_ci/* 106762306a36Sopenharmony_ci * On AArch64, the cache coherency is handled via the set_pte_at() function. 106862306a36Sopenharmony_ci */ 106962306a36Sopenharmony_cistatic inline void update_mmu_cache_range(struct vm_fault *vmf, 107062306a36Sopenharmony_ci struct vm_area_struct *vma, unsigned long addr, pte_t *ptep, 107162306a36Sopenharmony_ci unsigned int nr) 107262306a36Sopenharmony_ci{ 107362306a36Sopenharmony_ci /* 107462306a36Sopenharmony_ci * We don't do anything here, so there's a very small chance of 107562306a36Sopenharmony_ci * us retaking a user fault which we just fixed up. The alternative 107662306a36Sopenharmony_ci * is doing a dsb(ishst), but that penalises the fastpath. 107762306a36Sopenharmony_ci */ 107862306a36Sopenharmony_ci} 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_ci#define update_mmu_cache(vma, addr, ptep) \ 108162306a36Sopenharmony_ci update_mmu_cache_range(NULL, vma, addr, ptep, 1) 108262306a36Sopenharmony_ci#define update_mmu_cache_pmd(vma, address, pmd) do { } while (0) 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_ci#ifdef CONFIG_ARM64_PA_BITS_52 108562306a36Sopenharmony_ci#define phys_to_ttbr(addr) (((addr) | ((addr) >> 46)) & TTBR_BADDR_MASK_52) 108662306a36Sopenharmony_ci#else 108762306a36Sopenharmony_ci#define phys_to_ttbr(addr) (addr) 108862306a36Sopenharmony_ci#endif 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_ci/* 109162306a36Sopenharmony_ci * On arm64 without hardware Access Flag, copying from user will fail because 109262306a36Sopenharmony_ci * the pte is old and cannot be marked young. So we always end up with zeroed 109362306a36Sopenharmony_ci * page after fork() + CoW for pfn mappings. We don't always have a 109462306a36Sopenharmony_ci * hardware-managed access flag on arm64. 109562306a36Sopenharmony_ci */ 109662306a36Sopenharmony_ci#define arch_has_hw_pte_young cpu_has_hw_af 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_ci/* 109962306a36Sopenharmony_ci * Experimentally, it's cheap to set the access flag in hardware and we 110062306a36Sopenharmony_ci * benefit from prefaulting mappings as 'old' to start with. 110162306a36Sopenharmony_ci */ 110262306a36Sopenharmony_ci#define arch_wants_old_prefaulted_pte cpu_has_hw_af 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_cistatic inline bool pud_sect_supported(void) 110562306a36Sopenharmony_ci{ 110662306a36Sopenharmony_ci return PAGE_SIZE == SZ_4K; 110762306a36Sopenharmony_ci} 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_ci#define __HAVE_ARCH_PTEP_MODIFY_PROT_TRANSACTION 111162306a36Sopenharmony_ci#define ptep_modify_prot_start ptep_modify_prot_start 111262306a36Sopenharmony_ciextern pte_t ptep_modify_prot_start(struct vm_area_struct *vma, 111362306a36Sopenharmony_ci unsigned long addr, pte_t *ptep); 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_ci#define ptep_modify_prot_commit ptep_modify_prot_commit 111662306a36Sopenharmony_ciextern void ptep_modify_prot_commit(struct vm_area_struct *vma, 111762306a36Sopenharmony_ci unsigned long addr, pte_t *ptep, 111862306a36Sopenharmony_ci pte_t old_pte, pte_t new_pte); 111962306a36Sopenharmony_ci#endif /* !__ASSEMBLY__ */ 112062306a36Sopenharmony_ci 112162306a36Sopenharmony_ci#endif /* __ASM_PGTABLE_H */ 1122