162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * arch/arm64/mm/hugetlbpage.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2013 Linaro Ltd. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Based on arch/x86/mm/hugetlbpage.c. 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/init.h> 1162306a36Sopenharmony_ci#include <linux/fs.h> 1262306a36Sopenharmony_ci#include <linux/mm.h> 1362306a36Sopenharmony_ci#include <linux/hugetlb.h> 1462306a36Sopenharmony_ci#include <linux/pagemap.h> 1562306a36Sopenharmony_ci#include <linux/err.h> 1662306a36Sopenharmony_ci#include <linux/sysctl.h> 1762306a36Sopenharmony_ci#include <asm/mman.h> 1862306a36Sopenharmony_ci#include <asm/tlb.h> 1962306a36Sopenharmony_ci#include <asm/tlbflush.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci/* 2262306a36Sopenharmony_ci * HugeTLB Support Matrix 2362306a36Sopenharmony_ci * 2462306a36Sopenharmony_ci * --------------------------------------------------- 2562306a36Sopenharmony_ci * | Page Size | CONT PTE | PMD | CONT PMD | PUD | 2662306a36Sopenharmony_ci * --------------------------------------------------- 2762306a36Sopenharmony_ci * | 4K | 64K | 2M | 32M | 1G | 2862306a36Sopenharmony_ci * | 16K | 2M | 32M | 1G | | 2962306a36Sopenharmony_ci * | 64K | 2M | 512M | 16G | | 3062306a36Sopenharmony_ci * --------------------------------------------------- 3162306a36Sopenharmony_ci */ 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci/* 3462306a36Sopenharmony_ci * Reserve CMA areas for the largest supported gigantic 3562306a36Sopenharmony_ci * huge page when requested. Any other smaller gigantic 3662306a36Sopenharmony_ci * huge pages could still be served from those areas. 3762306a36Sopenharmony_ci */ 3862306a36Sopenharmony_ci#ifdef CONFIG_CMA 3962306a36Sopenharmony_civoid __init arm64_hugetlb_cma_reserve(void) 4062306a36Sopenharmony_ci{ 4162306a36Sopenharmony_ci int order; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci if (pud_sect_supported()) 4462306a36Sopenharmony_ci order = PUD_SHIFT - PAGE_SHIFT; 4562306a36Sopenharmony_ci else 4662306a36Sopenharmony_ci order = CONT_PMD_SHIFT - PAGE_SHIFT; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci /* 4962306a36Sopenharmony_ci * HugeTLB CMA reservation is required for gigantic 5062306a36Sopenharmony_ci * huge pages which could not be allocated via the 5162306a36Sopenharmony_ci * page allocator. Just warn if there is any change 5262306a36Sopenharmony_ci * breaking this assumption. 5362306a36Sopenharmony_ci */ 5462306a36Sopenharmony_ci WARN_ON(order <= MAX_ORDER); 5562306a36Sopenharmony_ci hugetlb_cma_reserve(order); 5662306a36Sopenharmony_ci} 5762306a36Sopenharmony_ci#endif /* CONFIG_CMA */ 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistatic bool __hugetlb_valid_size(unsigned long size) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci switch (size) { 6262306a36Sopenharmony_ci#ifndef __PAGETABLE_PMD_FOLDED 6362306a36Sopenharmony_ci case PUD_SIZE: 6462306a36Sopenharmony_ci return pud_sect_supported(); 6562306a36Sopenharmony_ci#endif 6662306a36Sopenharmony_ci case CONT_PMD_SIZE: 6762306a36Sopenharmony_ci case PMD_SIZE: 6862306a36Sopenharmony_ci case CONT_PTE_SIZE: 6962306a36Sopenharmony_ci return true; 7062306a36Sopenharmony_ci } 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci return false; 7362306a36Sopenharmony_ci} 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci#ifdef CONFIG_ARCH_ENABLE_HUGEPAGE_MIGRATION 7662306a36Sopenharmony_cibool arch_hugetlb_migration_supported(struct hstate *h) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci size_t pagesize = huge_page_size(h); 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci if (!__hugetlb_valid_size(pagesize)) { 8162306a36Sopenharmony_ci pr_warn("%s: unrecognized huge page size 0x%lx\n", 8262306a36Sopenharmony_ci __func__, pagesize); 8362306a36Sopenharmony_ci return false; 8462306a36Sopenharmony_ci } 8562306a36Sopenharmony_ci return true; 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci#endif 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ciint pmd_huge(pmd_t pmd) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci return pmd_val(pmd) && !(pmd_val(pmd) & PMD_TABLE_BIT); 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ciint pud_huge(pud_t pud) 9562306a36Sopenharmony_ci{ 9662306a36Sopenharmony_ci#ifndef __PAGETABLE_PMD_FOLDED 9762306a36Sopenharmony_ci return pud_val(pud) && !(pud_val(pud) & PUD_TABLE_BIT); 9862306a36Sopenharmony_ci#else 9962306a36Sopenharmony_ci return 0; 10062306a36Sopenharmony_ci#endif 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_cistatic int find_num_contig(struct mm_struct *mm, unsigned long addr, 10462306a36Sopenharmony_ci pte_t *ptep, size_t *pgsize) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci pgd_t *pgdp = pgd_offset(mm, addr); 10762306a36Sopenharmony_ci p4d_t *p4dp; 10862306a36Sopenharmony_ci pud_t *pudp; 10962306a36Sopenharmony_ci pmd_t *pmdp; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci *pgsize = PAGE_SIZE; 11262306a36Sopenharmony_ci p4dp = p4d_offset(pgdp, addr); 11362306a36Sopenharmony_ci pudp = pud_offset(p4dp, addr); 11462306a36Sopenharmony_ci pmdp = pmd_offset(pudp, addr); 11562306a36Sopenharmony_ci if ((pte_t *)pmdp == ptep) { 11662306a36Sopenharmony_ci *pgsize = PMD_SIZE; 11762306a36Sopenharmony_ci return CONT_PMDS; 11862306a36Sopenharmony_ci } 11962306a36Sopenharmony_ci return CONT_PTES; 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic inline int num_contig_ptes(unsigned long size, size_t *pgsize) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci int contig_ptes = 0; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci *pgsize = size; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci switch (size) { 12962306a36Sopenharmony_ci#ifndef __PAGETABLE_PMD_FOLDED 13062306a36Sopenharmony_ci case PUD_SIZE: 13162306a36Sopenharmony_ci if (pud_sect_supported()) 13262306a36Sopenharmony_ci contig_ptes = 1; 13362306a36Sopenharmony_ci break; 13462306a36Sopenharmony_ci#endif 13562306a36Sopenharmony_ci case PMD_SIZE: 13662306a36Sopenharmony_ci contig_ptes = 1; 13762306a36Sopenharmony_ci break; 13862306a36Sopenharmony_ci case CONT_PMD_SIZE: 13962306a36Sopenharmony_ci *pgsize = PMD_SIZE; 14062306a36Sopenharmony_ci contig_ptes = CONT_PMDS; 14162306a36Sopenharmony_ci break; 14262306a36Sopenharmony_ci case CONT_PTE_SIZE: 14362306a36Sopenharmony_ci *pgsize = PAGE_SIZE; 14462306a36Sopenharmony_ci contig_ptes = CONT_PTES; 14562306a36Sopenharmony_ci break; 14662306a36Sopenharmony_ci } 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci return contig_ptes; 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_cipte_t huge_ptep_get(pte_t *ptep) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci int ncontig, i; 15462306a36Sopenharmony_ci size_t pgsize; 15562306a36Sopenharmony_ci pte_t orig_pte = ptep_get(ptep); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci if (!pte_present(orig_pte) || !pte_cont(orig_pte)) 15862306a36Sopenharmony_ci return orig_pte; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci ncontig = num_contig_ptes(page_size(pte_page(orig_pte)), &pgsize); 16162306a36Sopenharmony_ci for (i = 0; i < ncontig; i++, ptep++) { 16262306a36Sopenharmony_ci pte_t pte = ptep_get(ptep); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci if (pte_dirty(pte)) 16562306a36Sopenharmony_ci orig_pte = pte_mkdirty(orig_pte); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci if (pte_young(pte)) 16862306a36Sopenharmony_ci orig_pte = pte_mkyoung(orig_pte); 16962306a36Sopenharmony_ci } 17062306a36Sopenharmony_ci return orig_pte; 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci/* 17462306a36Sopenharmony_ci * Changing some bits of contiguous entries requires us to follow a 17562306a36Sopenharmony_ci * Break-Before-Make approach, breaking the whole contiguous set 17662306a36Sopenharmony_ci * before we can change any entries. See ARM DDI 0487A.k_iss10775, 17762306a36Sopenharmony_ci * "Misprogramming of the Contiguous bit", page D4-1762. 17862306a36Sopenharmony_ci * 17962306a36Sopenharmony_ci * This helper performs the break step. 18062306a36Sopenharmony_ci */ 18162306a36Sopenharmony_cistatic pte_t get_clear_contig(struct mm_struct *mm, 18262306a36Sopenharmony_ci unsigned long addr, 18362306a36Sopenharmony_ci pte_t *ptep, 18462306a36Sopenharmony_ci unsigned long pgsize, 18562306a36Sopenharmony_ci unsigned long ncontig) 18662306a36Sopenharmony_ci{ 18762306a36Sopenharmony_ci pte_t orig_pte = ptep_get(ptep); 18862306a36Sopenharmony_ci unsigned long i; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci for (i = 0; i < ncontig; i++, addr += pgsize, ptep++) { 19162306a36Sopenharmony_ci pte_t pte = ptep_get_and_clear(mm, addr, ptep); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci /* 19462306a36Sopenharmony_ci * If HW_AFDBM is enabled, then the HW could turn on 19562306a36Sopenharmony_ci * the dirty or accessed bit for any page in the set, 19662306a36Sopenharmony_ci * so check them all. 19762306a36Sopenharmony_ci */ 19862306a36Sopenharmony_ci if (pte_dirty(pte)) 19962306a36Sopenharmony_ci orig_pte = pte_mkdirty(orig_pte); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci if (pte_young(pte)) 20262306a36Sopenharmony_ci orig_pte = pte_mkyoung(orig_pte); 20362306a36Sopenharmony_ci } 20462306a36Sopenharmony_ci return orig_pte; 20562306a36Sopenharmony_ci} 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_cistatic pte_t get_clear_contig_flush(struct mm_struct *mm, 20862306a36Sopenharmony_ci unsigned long addr, 20962306a36Sopenharmony_ci pte_t *ptep, 21062306a36Sopenharmony_ci unsigned long pgsize, 21162306a36Sopenharmony_ci unsigned long ncontig) 21262306a36Sopenharmony_ci{ 21362306a36Sopenharmony_ci pte_t orig_pte = get_clear_contig(mm, addr, ptep, pgsize, ncontig); 21462306a36Sopenharmony_ci struct vm_area_struct vma = TLB_FLUSH_VMA(mm, 0); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci flush_tlb_range(&vma, addr, addr + (pgsize * ncontig)); 21762306a36Sopenharmony_ci return orig_pte; 21862306a36Sopenharmony_ci} 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci/* 22162306a36Sopenharmony_ci * Changing some bits of contiguous entries requires us to follow a 22262306a36Sopenharmony_ci * Break-Before-Make approach, breaking the whole contiguous set 22362306a36Sopenharmony_ci * before we can change any entries. See ARM DDI 0487A.k_iss10775, 22462306a36Sopenharmony_ci * "Misprogramming of the Contiguous bit", page D4-1762. 22562306a36Sopenharmony_ci * 22662306a36Sopenharmony_ci * This helper performs the break step for use cases where the 22762306a36Sopenharmony_ci * original pte is not needed. 22862306a36Sopenharmony_ci */ 22962306a36Sopenharmony_cistatic void clear_flush(struct mm_struct *mm, 23062306a36Sopenharmony_ci unsigned long addr, 23162306a36Sopenharmony_ci pte_t *ptep, 23262306a36Sopenharmony_ci unsigned long pgsize, 23362306a36Sopenharmony_ci unsigned long ncontig) 23462306a36Sopenharmony_ci{ 23562306a36Sopenharmony_ci struct vm_area_struct vma = TLB_FLUSH_VMA(mm, 0); 23662306a36Sopenharmony_ci unsigned long i, saddr = addr; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci for (i = 0; i < ncontig; i++, addr += pgsize, ptep++) 23962306a36Sopenharmony_ci ptep_clear(mm, addr, ptep); 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci flush_tlb_range(&vma, saddr, addr); 24262306a36Sopenharmony_ci} 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_civoid set_huge_pte_at(struct mm_struct *mm, unsigned long addr, 24562306a36Sopenharmony_ci pte_t *ptep, pte_t pte, unsigned long sz) 24662306a36Sopenharmony_ci{ 24762306a36Sopenharmony_ci size_t pgsize; 24862306a36Sopenharmony_ci int i; 24962306a36Sopenharmony_ci int ncontig; 25062306a36Sopenharmony_ci unsigned long pfn, dpfn; 25162306a36Sopenharmony_ci pgprot_t hugeprot; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci ncontig = num_contig_ptes(sz, &pgsize); 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci if (!pte_present(pte)) { 25662306a36Sopenharmony_ci for (i = 0; i < ncontig; i++, ptep++, addr += pgsize) 25762306a36Sopenharmony_ci set_pte_at(mm, addr, ptep, pte); 25862306a36Sopenharmony_ci return; 25962306a36Sopenharmony_ci } 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci if (!pte_cont(pte)) { 26262306a36Sopenharmony_ci set_pte_at(mm, addr, ptep, pte); 26362306a36Sopenharmony_ci return; 26462306a36Sopenharmony_ci } 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci pfn = pte_pfn(pte); 26762306a36Sopenharmony_ci dpfn = pgsize >> PAGE_SHIFT; 26862306a36Sopenharmony_ci hugeprot = pte_pgprot(pte); 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci clear_flush(mm, addr, ptep, pgsize, ncontig); 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci for (i = 0; i < ncontig; i++, ptep++, addr += pgsize, pfn += dpfn) 27362306a36Sopenharmony_ci set_pte_at(mm, addr, ptep, pfn_pte(pfn, hugeprot)); 27462306a36Sopenharmony_ci} 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_cipte_t *huge_pte_alloc(struct mm_struct *mm, struct vm_area_struct *vma, 27762306a36Sopenharmony_ci unsigned long addr, unsigned long sz) 27862306a36Sopenharmony_ci{ 27962306a36Sopenharmony_ci pgd_t *pgdp; 28062306a36Sopenharmony_ci p4d_t *p4dp; 28162306a36Sopenharmony_ci pud_t *pudp; 28262306a36Sopenharmony_ci pmd_t *pmdp; 28362306a36Sopenharmony_ci pte_t *ptep = NULL; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci pgdp = pgd_offset(mm, addr); 28662306a36Sopenharmony_ci p4dp = p4d_offset(pgdp, addr); 28762306a36Sopenharmony_ci pudp = pud_alloc(mm, p4dp, addr); 28862306a36Sopenharmony_ci if (!pudp) 28962306a36Sopenharmony_ci return NULL; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci if (sz == PUD_SIZE) { 29262306a36Sopenharmony_ci ptep = (pte_t *)pudp; 29362306a36Sopenharmony_ci } else if (sz == (CONT_PTE_SIZE)) { 29462306a36Sopenharmony_ci pmdp = pmd_alloc(mm, pudp, addr); 29562306a36Sopenharmony_ci if (!pmdp) 29662306a36Sopenharmony_ci return NULL; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci WARN_ON(addr & (sz - 1)); 29962306a36Sopenharmony_ci ptep = pte_alloc_huge(mm, pmdp, addr); 30062306a36Sopenharmony_ci } else if (sz == PMD_SIZE) { 30162306a36Sopenharmony_ci if (want_pmd_share(vma, addr) && pud_none(READ_ONCE(*pudp))) 30262306a36Sopenharmony_ci ptep = huge_pmd_share(mm, vma, addr, pudp); 30362306a36Sopenharmony_ci else 30462306a36Sopenharmony_ci ptep = (pte_t *)pmd_alloc(mm, pudp, addr); 30562306a36Sopenharmony_ci } else if (sz == (CONT_PMD_SIZE)) { 30662306a36Sopenharmony_ci pmdp = pmd_alloc(mm, pudp, addr); 30762306a36Sopenharmony_ci WARN_ON(addr & (sz - 1)); 30862306a36Sopenharmony_ci return (pte_t *)pmdp; 30962306a36Sopenharmony_ci } 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci return ptep; 31262306a36Sopenharmony_ci} 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_cipte_t *huge_pte_offset(struct mm_struct *mm, 31562306a36Sopenharmony_ci unsigned long addr, unsigned long sz) 31662306a36Sopenharmony_ci{ 31762306a36Sopenharmony_ci pgd_t *pgdp; 31862306a36Sopenharmony_ci p4d_t *p4dp; 31962306a36Sopenharmony_ci pud_t *pudp, pud; 32062306a36Sopenharmony_ci pmd_t *pmdp, pmd; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci pgdp = pgd_offset(mm, addr); 32362306a36Sopenharmony_ci if (!pgd_present(READ_ONCE(*pgdp))) 32462306a36Sopenharmony_ci return NULL; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci p4dp = p4d_offset(pgdp, addr); 32762306a36Sopenharmony_ci if (!p4d_present(READ_ONCE(*p4dp))) 32862306a36Sopenharmony_ci return NULL; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci pudp = pud_offset(p4dp, addr); 33162306a36Sopenharmony_ci pud = READ_ONCE(*pudp); 33262306a36Sopenharmony_ci if (sz != PUD_SIZE && pud_none(pud)) 33362306a36Sopenharmony_ci return NULL; 33462306a36Sopenharmony_ci /* hugepage or swap? */ 33562306a36Sopenharmony_ci if (pud_huge(pud) || !pud_present(pud)) 33662306a36Sopenharmony_ci return (pte_t *)pudp; 33762306a36Sopenharmony_ci /* table; check the next level */ 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci if (sz == CONT_PMD_SIZE) 34062306a36Sopenharmony_ci addr &= CONT_PMD_MASK; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci pmdp = pmd_offset(pudp, addr); 34362306a36Sopenharmony_ci pmd = READ_ONCE(*pmdp); 34462306a36Sopenharmony_ci if (!(sz == PMD_SIZE || sz == CONT_PMD_SIZE) && 34562306a36Sopenharmony_ci pmd_none(pmd)) 34662306a36Sopenharmony_ci return NULL; 34762306a36Sopenharmony_ci if (pmd_huge(pmd) || !pmd_present(pmd)) 34862306a36Sopenharmony_ci return (pte_t *)pmdp; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci if (sz == CONT_PTE_SIZE) 35162306a36Sopenharmony_ci return pte_offset_huge(pmdp, (addr & CONT_PTE_MASK)); 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci return NULL; 35462306a36Sopenharmony_ci} 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ciunsigned long hugetlb_mask_last_page(struct hstate *h) 35762306a36Sopenharmony_ci{ 35862306a36Sopenharmony_ci unsigned long hp_size = huge_page_size(h); 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci switch (hp_size) { 36162306a36Sopenharmony_ci#ifndef __PAGETABLE_PMD_FOLDED 36262306a36Sopenharmony_ci case PUD_SIZE: 36362306a36Sopenharmony_ci return PGDIR_SIZE - PUD_SIZE; 36462306a36Sopenharmony_ci#endif 36562306a36Sopenharmony_ci case CONT_PMD_SIZE: 36662306a36Sopenharmony_ci return PUD_SIZE - CONT_PMD_SIZE; 36762306a36Sopenharmony_ci case PMD_SIZE: 36862306a36Sopenharmony_ci return PUD_SIZE - PMD_SIZE; 36962306a36Sopenharmony_ci case CONT_PTE_SIZE: 37062306a36Sopenharmony_ci return PMD_SIZE - CONT_PTE_SIZE; 37162306a36Sopenharmony_ci default: 37262306a36Sopenharmony_ci break; 37362306a36Sopenharmony_ci } 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci return 0UL; 37662306a36Sopenharmony_ci} 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_cipte_t arch_make_huge_pte(pte_t entry, unsigned int shift, vm_flags_t flags) 37962306a36Sopenharmony_ci{ 38062306a36Sopenharmony_ci size_t pagesize = 1UL << shift; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci entry = pte_mkhuge(entry); 38362306a36Sopenharmony_ci if (pagesize == CONT_PTE_SIZE) { 38462306a36Sopenharmony_ci entry = pte_mkcont(entry); 38562306a36Sopenharmony_ci } else if (pagesize == CONT_PMD_SIZE) { 38662306a36Sopenharmony_ci entry = pmd_pte(pmd_mkcont(pte_pmd(entry))); 38762306a36Sopenharmony_ci } else if (pagesize != PUD_SIZE && pagesize != PMD_SIZE) { 38862306a36Sopenharmony_ci pr_warn("%s: unrecognized huge page size 0x%lx\n", 38962306a36Sopenharmony_ci __func__, pagesize); 39062306a36Sopenharmony_ci } 39162306a36Sopenharmony_ci return entry; 39262306a36Sopenharmony_ci} 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_civoid huge_pte_clear(struct mm_struct *mm, unsigned long addr, 39562306a36Sopenharmony_ci pte_t *ptep, unsigned long sz) 39662306a36Sopenharmony_ci{ 39762306a36Sopenharmony_ci int i, ncontig; 39862306a36Sopenharmony_ci size_t pgsize; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci ncontig = num_contig_ptes(sz, &pgsize); 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci for (i = 0; i < ncontig; i++, addr += pgsize, ptep++) 40362306a36Sopenharmony_ci pte_clear(mm, addr, ptep); 40462306a36Sopenharmony_ci} 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_cipte_t huge_ptep_get_and_clear(struct mm_struct *mm, 40762306a36Sopenharmony_ci unsigned long addr, pte_t *ptep) 40862306a36Sopenharmony_ci{ 40962306a36Sopenharmony_ci int ncontig; 41062306a36Sopenharmony_ci size_t pgsize; 41162306a36Sopenharmony_ci pte_t orig_pte = ptep_get(ptep); 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci if (!pte_cont(orig_pte)) 41462306a36Sopenharmony_ci return ptep_get_and_clear(mm, addr, ptep); 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci ncontig = find_num_contig(mm, addr, ptep, &pgsize); 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci return get_clear_contig(mm, addr, ptep, pgsize, ncontig); 41962306a36Sopenharmony_ci} 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci/* 42262306a36Sopenharmony_ci * huge_ptep_set_access_flags will update access flags (dirty, accesssed) 42362306a36Sopenharmony_ci * and write permission. 42462306a36Sopenharmony_ci * 42562306a36Sopenharmony_ci * For a contiguous huge pte range we need to check whether or not write 42662306a36Sopenharmony_ci * permission has to change only on the first pte in the set. Then for 42762306a36Sopenharmony_ci * all the contiguous ptes we need to check whether or not there is a 42862306a36Sopenharmony_ci * discrepancy between dirty or young. 42962306a36Sopenharmony_ci */ 43062306a36Sopenharmony_cistatic int __cont_access_flags_changed(pte_t *ptep, pte_t pte, int ncontig) 43162306a36Sopenharmony_ci{ 43262306a36Sopenharmony_ci int i; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci if (pte_write(pte) != pte_write(ptep_get(ptep))) 43562306a36Sopenharmony_ci return 1; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci for (i = 0; i < ncontig; i++) { 43862306a36Sopenharmony_ci pte_t orig_pte = ptep_get(ptep + i); 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci if (pte_dirty(pte) != pte_dirty(orig_pte)) 44162306a36Sopenharmony_ci return 1; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci if (pte_young(pte) != pte_young(orig_pte)) 44462306a36Sopenharmony_ci return 1; 44562306a36Sopenharmony_ci } 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci return 0; 44862306a36Sopenharmony_ci} 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ciint huge_ptep_set_access_flags(struct vm_area_struct *vma, 45162306a36Sopenharmony_ci unsigned long addr, pte_t *ptep, 45262306a36Sopenharmony_ci pte_t pte, int dirty) 45362306a36Sopenharmony_ci{ 45462306a36Sopenharmony_ci int ncontig, i; 45562306a36Sopenharmony_ci size_t pgsize = 0; 45662306a36Sopenharmony_ci unsigned long pfn = pte_pfn(pte), dpfn; 45762306a36Sopenharmony_ci struct mm_struct *mm = vma->vm_mm; 45862306a36Sopenharmony_ci pgprot_t hugeprot; 45962306a36Sopenharmony_ci pte_t orig_pte; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci if (!pte_cont(pte)) 46262306a36Sopenharmony_ci return ptep_set_access_flags(vma, addr, ptep, pte, dirty); 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci ncontig = find_num_contig(mm, addr, ptep, &pgsize); 46562306a36Sopenharmony_ci dpfn = pgsize >> PAGE_SHIFT; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci if (!__cont_access_flags_changed(ptep, pte, ncontig)) 46862306a36Sopenharmony_ci return 0; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci orig_pte = get_clear_contig_flush(mm, addr, ptep, pgsize, ncontig); 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci /* Make sure we don't lose the dirty or young state */ 47362306a36Sopenharmony_ci if (pte_dirty(orig_pte)) 47462306a36Sopenharmony_ci pte = pte_mkdirty(pte); 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci if (pte_young(orig_pte)) 47762306a36Sopenharmony_ci pte = pte_mkyoung(pte); 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci hugeprot = pte_pgprot(pte); 48062306a36Sopenharmony_ci for (i = 0; i < ncontig; i++, ptep++, addr += pgsize, pfn += dpfn) 48162306a36Sopenharmony_ci set_pte_at(mm, addr, ptep, pfn_pte(pfn, hugeprot)); 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci return 1; 48462306a36Sopenharmony_ci} 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_civoid huge_ptep_set_wrprotect(struct mm_struct *mm, 48762306a36Sopenharmony_ci unsigned long addr, pte_t *ptep) 48862306a36Sopenharmony_ci{ 48962306a36Sopenharmony_ci unsigned long pfn, dpfn; 49062306a36Sopenharmony_ci pgprot_t hugeprot; 49162306a36Sopenharmony_ci int ncontig, i; 49262306a36Sopenharmony_ci size_t pgsize; 49362306a36Sopenharmony_ci pte_t pte; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci if (!pte_cont(READ_ONCE(*ptep))) { 49662306a36Sopenharmony_ci ptep_set_wrprotect(mm, addr, ptep); 49762306a36Sopenharmony_ci return; 49862306a36Sopenharmony_ci } 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci ncontig = find_num_contig(mm, addr, ptep, &pgsize); 50162306a36Sopenharmony_ci dpfn = pgsize >> PAGE_SHIFT; 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci pte = get_clear_contig_flush(mm, addr, ptep, pgsize, ncontig); 50462306a36Sopenharmony_ci pte = pte_wrprotect(pte); 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci hugeprot = pte_pgprot(pte); 50762306a36Sopenharmony_ci pfn = pte_pfn(pte); 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci for (i = 0; i < ncontig; i++, ptep++, addr += pgsize, pfn += dpfn) 51062306a36Sopenharmony_ci set_pte_at(mm, addr, ptep, pfn_pte(pfn, hugeprot)); 51162306a36Sopenharmony_ci} 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_cipte_t huge_ptep_clear_flush(struct vm_area_struct *vma, 51462306a36Sopenharmony_ci unsigned long addr, pte_t *ptep) 51562306a36Sopenharmony_ci{ 51662306a36Sopenharmony_ci struct mm_struct *mm = vma->vm_mm; 51762306a36Sopenharmony_ci size_t pgsize; 51862306a36Sopenharmony_ci int ncontig; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci if (!pte_cont(READ_ONCE(*ptep))) 52162306a36Sopenharmony_ci return ptep_clear_flush(vma, addr, ptep); 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci ncontig = find_num_contig(mm, addr, ptep, &pgsize); 52462306a36Sopenharmony_ci return get_clear_contig_flush(mm, addr, ptep, pgsize, ncontig); 52562306a36Sopenharmony_ci} 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_cistatic int __init hugetlbpage_init(void) 52862306a36Sopenharmony_ci{ 52962306a36Sopenharmony_ci if (pud_sect_supported()) 53062306a36Sopenharmony_ci hugetlb_add_hstate(PUD_SHIFT - PAGE_SHIFT); 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci hugetlb_add_hstate(CONT_PMD_SHIFT - PAGE_SHIFT); 53362306a36Sopenharmony_ci hugetlb_add_hstate(PMD_SHIFT - PAGE_SHIFT); 53462306a36Sopenharmony_ci hugetlb_add_hstate(CONT_PTE_SHIFT - PAGE_SHIFT); 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci return 0; 53762306a36Sopenharmony_ci} 53862306a36Sopenharmony_ciarch_initcall(hugetlbpage_init); 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_cibool __init arch_hugetlb_valid_size(unsigned long size) 54162306a36Sopenharmony_ci{ 54262306a36Sopenharmony_ci return __hugetlb_valid_size(size); 54362306a36Sopenharmony_ci} 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_cipte_t huge_ptep_modify_prot_start(struct vm_area_struct *vma, unsigned long addr, pte_t *ptep) 54662306a36Sopenharmony_ci{ 54762306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_ARM64_ERRATUM_2645198) && 54862306a36Sopenharmony_ci cpus_have_const_cap(ARM64_WORKAROUND_2645198)) { 54962306a36Sopenharmony_ci /* 55062306a36Sopenharmony_ci * Break-before-make (BBM) is required for all user space mappings 55162306a36Sopenharmony_ci * when the permission changes from executable to non-executable 55262306a36Sopenharmony_ci * in cases where cpu is affected with errata #2645198. 55362306a36Sopenharmony_ci */ 55462306a36Sopenharmony_ci if (pte_user_exec(READ_ONCE(*ptep))) 55562306a36Sopenharmony_ci return huge_ptep_clear_flush(vma, addr, ptep); 55662306a36Sopenharmony_ci } 55762306a36Sopenharmony_ci return huge_ptep_get_and_clear(vma->vm_mm, addr, ptep); 55862306a36Sopenharmony_ci} 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_civoid huge_ptep_modify_prot_commit(struct vm_area_struct *vma, unsigned long addr, pte_t *ptep, 56162306a36Sopenharmony_ci pte_t old_pte, pte_t pte) 56262306a36Sopenharmony_ci{ 56362306a36Sopenharmony_ci unsigned long psize = huge_page_size(hstate_vma(vma)); 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci set_huge_pte_at(vma->vm_mm, addr, ptep, pte, psize); 56662306a36Sopenharmony_ci} 567