11802d0beSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2084bd298SSteve Capper /* 3084bd298SSteve Capper * arch/arm64/mm/hugetlbpage.c 4084bd298SSteve Capper * 5084bd298SSteve Capper * Copyright (C) 2013 Linaro Ltd. 6084bd298SSteve Capper * 7084bd298SSteve Capper * Based on arch/x86/mm/hugetlbpage.c. 8084bd298SSteve Capper */ 9084bd298SSteve Capper 10084bd298SSteve Capper #include <linux/init.h> 11084bd298SSteve Capper #include <linux/fs.h> 12084bd298SSteve Capper #include <linux/mm.h> 13084bd298SSteve Capper #include <linux/hugetlb.h> 14084bd298SSteve Capper #include <linux/pagemap.h> 15084bd298SSteve Capper #include <linux/err.h> 16084bd298SSteve Capper #include <linux/sysctl.h> 17084bd298SSteve Capper #include <asm/mman.h> 18084bd298SSteve Capper #include <asm/tlb.h> 19084bd298SSteve Capper #include <asm/tlbflush.h> 20084bd298SSteve Capper 21abb7962aSAnshuman Khandual /* 22abb7962aSAnshuman Khandual * HugeTLB Support Matrix 23abb7962aSAnshuman Khandual * 24abb7962aSAnshuman Khandual * --------------------------------------------------- 25abb7962aSAnshuman Khandual * | Page Size | CONT PTE | PMD | CONT PMD | PUD | 26abb7962aSAnshuman Khandual * --------------------------------------------------- 27abb7962aSAnshuman Khandual * | 4K | 64K | 2M | 32M | 1G | 28abb7962aSAnshuman Khandual * | 16K | 2M | 32M | 1G | | 29abb7962aSAnshuman Khandual * | 64K | 2M | 512M | 16G | | 30abb7962aSAnshuman Khandual * --------------------------------------------------- 31abb7962aSAnshuman Khandual */ 32abb7962aSAnshuman Khandual 33abb7962aSAnshuman Khandual /* 34abb7962aSAnshuman Khandual * Reserve CMA areas for the largest supported gigantic 35abb7962aSAnshuman Khandual * huge page when requested. Any other smaller gigantic 36abb7962aSAnshuman Khandual * huge pages could still be served from those areas. 37abb7962aSAnshuman Khandual */ 38abb7962aSAnshuman Khandual #ifdef CONFIG_CMA 39abb7962aSAnshuman Khandual void __init arm64_hugetlb_cma_reserve(void) 40abb7962aSAnshuman Khandual { 41abb7962aSAnshuman Khandual int order; 42abb7962aSAnshuman Khandual 43f8b46c4bSAnshuman Khandual if (pud_sect_supported()) 44abb7962aSAnshuman Khandual order = PUD_SHIFT - PAGE_SHIFT; 45f8b46c4bSAnshuman Khandual else 462e5809a4SMike Kravetz order = CONT_PMD_SHIFT - PAGE_SHIFT; 47e6359798SWill Deacon 48abb7962aSAnshuman Khandual hugetlb_cma_reserve(order); 49abb7962aSAnshuman Khandual } 50abb7962aSAnshuman Khandual #endif /* CONFIG_CMA */ 51abb7962aSAnshuman Khandual 52a8a733b2SAnshuman Khandual static bool __hugetlb_valid_size(unsigned long size) 53a8a733b2SAnshuman Khandual { 54a8a733b2SAnshuman Khandual switch (size) { 55a8a733b2SAnshuman Khandual #ifndef __PAGETABLE_PMD_FOLDED 56a8a733b2SAnshuman Khandual case PUD_SIZE: 57a8a733b2SAnshuman Khandual return pud_sect_supported(); 58a8a733b2SAnshuman Khandual #endif 59a8a733b2SAnshuman Khandual case CONT_PMD_SIZE: 60a8a733b2SAnshuman Khandual case PMD_SIZE: 61a8a733b2SAnshuman Khandual case CONT_PTE_SIZE: 62a8a733b2SAnshuman Khandual return true; 63a8a733b2SAnshuman Khandual } 64a8a733b2SAnshuman Khandual 65a8a733b2SAnshuman Khandual return false; 66a8a733b2SAnshuman Khandual } 67a8a733b2SAnshuman Khandual 685480280dSAnshuman Khandual #ifdef CONFIG_ARCH_ENABLE_HUGEPAGE_MIGRATION 695480280dSAnshuman Khandual bool arch_hugetlb_migration_supported(struct hstate *h) 705480280dSAnshuman Khandual { 715480280dSAnshuman Khandual size_t pagesize = huge_page_size(h); 725480280dSAnshuman Khandual 73a8a733b2SAnshuman Khandual if (!__hugetlb_valid_size(pagesize)) { 745480280dSAnshuman Khandual pr_warn("%s: unrecognized huge page size 0x%lx\n", 755480280dSAnshuman Khandual __func__, pagesize); 765480280dSAnshuman Khandual return false; 775480280dSAnshuman Khandual } 78a8a733b2SAnshuman Khandual return true; 79a8a733b2SAnshuman Khandual } 805480280dSAnshuman Khandual #endif 815480280dSAnshuman Khandual 8266b3923aSDavid Woods static int find_num_contig(struct mm_struct *mm, unsigned long addr, 83bb9dd3dfSSteve Capper pte_t *ptep, size_t *pgsize) 8466b3923aSDavid Woods { 8520a004e7SWill Deacon pgd_t *pgdp = pgd_offset(mm, addr); 86e9f63768SMike Rapoport p4d_t *p4dp; 8720a004e7SWill Deacon pud_t *pudp; 8820a004e7SWill Deacon pmd_t *pmdp; 8966b3923aSDavid Woods 9066b3923aSDavid Woods *pgsize = PAGE_SIZE; 91e9f63768SMike Rapoport p4dp = p4d_offset(pgdp, addr); 92e9f63768SMike Rapoport pudp = pud_offset(p4dp, addr); 9320a004e7SWill Deacon pmdp = pmd_offset(pudp, addr); 9420a004e7SWill Deacon if ((pte_t *)pmdp == ptep) { 9566b3923aSDavid Woods *pgsize = PMD_SIZE; 9666b3923aSDavid Woods return CONT_PMDS; 9766b3923aSDavid Woods } 9866b3923aSDavid Woods return CONT_PTES; 9966b3923aSDavid Woods } 10066b3923aSDavid Woods 101c3e4ed5cSPunit Agrawal static inline int num_contig_ptes(unsigned long size, size_t *pgsize) 102c3e4ed5cSPunit Agrawal { 10349c87f76SRyan Roberts int contig_ptes = 1; 104c3e4ed5cSPunit Agrawal 105c3e4ed5cSPunit Agrawal *pgsize = size; 106c3e4ed5cSPunit Agrawal 107c3e4ed5cSPunit Agrawal switch (size) { 108c3e4ed5cSPunit Agrawal case CONT_PMD_SIZE: 109c3e4ed5cSPunit Agrawal *pgsize = PMD_SIZE; 110c3e4ed5cSPunit Agrawal contig_ptes = CONT_PMDS; 111c3e4ed5cSPunit Agrawal break; 112c3e4ed5cSPunit Agrawal case CONT_PTE_SIZE: 113c3e4ed5cSPunit Agrawal *pgsize = PAGE_SIZE; 114c3e4ed5cSPunit Agrawal contig_ptes = CONT_PTES; 115c3e4ed5cSPunit Agrawal break; 11649c87f76SRyan Roberts default: 11749c87f76SRyan Roberts WARN_ON(!__hugetlb_valid_size(size)); 118c3e4ed5cSPunit Agrawal } 119c3e4ed5cSPunit Agrawal 120c3e4ed5cSPunit Agrawal return contig_ptes; 121c3e4ed5cSPunit Agrawal } 122c3e4ed5cSPunit Agrawal 123e6c0c032SChristophe Leroy pte_t huge_ptep_get(struct mm_struct *mm, unsigned long addr, pte_t *ptep) 124bc5dfb4fSBaolin Wang { 125bc5dfb4fSBaolin Wang int ncontig, i; 126bc5dfb4fSBaolin Wang size_t pgsize; 1275a00bfd6SRyan Roberts pte_t orig_pte = __ptep_get(ptep); 128bc5dfb4fSBaolin Wang 129bc5dfb4fSBaolin Wang if (!pte_present(orig_pte) || !pte_cont(orig_pte)) 130bc5dfb4fSBaolin Wang return orig_pte; 131bc5dfb4fSBaolin Wang 13229cb8051SRyan Roberts ncontig = find_num_contig(mm, addr, ptep, &pgsize); 133bc5dfb4fSBaolin Wang for (i = 0; i < ncontig; i++, ptep++) { 1345a00bfd6SRyan Roberts pte_t pte = __ptep_get(ptep); 135bc5dfb4fSBaolin Wang 136bc5dfb4fSBaolin Wang if (pte_dirty(pte)) 137bc5dfb4fSBaolin Wang orig_pte = pte_mkdirty(orig_pte); 138bc5dfb4fSBaolin Wang 139bc5dfb4fSBaolin Wang if (pte_young(pte)) 140bc5dfb4fSBaolin Wang orig_pte = pte_mkyoung(orig_pte); 141bc5dfb4fSBaolin Wang } 142bc5dfb4fSBaolin Wang return orig_pte; 143bc5dfb4fSBaolin Wang } 144bc5dfb4fSBaolin Wang 145d8bdcff2SSteve Capper /* 146d8bdcff2SSteve Capper * Changing some bits of contiguous entries requires us to follow a 147d8bdcff2SSteve Capper * Break-Before-Make approach, breaking the whole contiguous set 148d8bdcff2SSteve Capper * before we can change any entries. See ARM DDI 0487A.k_iss10775, 149d8bdcff2SSteve Capper * "Misprogramming of the Contiguous bit", page D4-1762. 150d8bdcff2SSteve Capper * 151d8bdcff2SSteve Capper * This helper performs the break step. 152d8bdcff2SSteve Capper */ 153fb396bb4SAnshuman Khandual static pte_t get_clear_contig(struct mm_struct *mm, 154d8bdcff2SSteve Capper unsigned long addr, 155d8bdcff2SSteve Capper pte_t *ptep, 156d8bdcff2SSteve Capper unsigned long pgsize, 157d8bdcff2SSteve Capper unsigned long ncontig) 158d8bdcff2SSteve Capper { 15949c87f76SRyan Roberts pte_t pte, tmp_pte; 16049c87f76SRyan Roberts bool present; 161d8bdcff2SSteve Capper 162*a899b7d0SRyan Roberts pte = __ptep_get_and_clear_anysz(mm, ptep, pgsize); 16349c87f76SRyan Roberts present = pte_present(pte); 16449c87f76SRyan Roberts while (--ncontig) { 16549c87f76SRyan Roberts ptep++; 166*a899b7d0SRyan Roberts tmp_pte = __ptep_get_and_clear_anysz(mm, ptep, pgsize); 16749c87f76SRyan Roberts if (present) { 16849c87f76SRyan Roberts if (pte_dirty(tmp_pte)) 16949c87f76SRyan Roberts pte = pte_mkdirty(pte); 17049c87f76SRyan Roberts if (pte_young(tmp_pte)) 17149c87f76SRyan Roberts pte = pte_mkyoung(pte); 172d8bdcff2SSteve Capper } 17349c87f76SRyan Roberts } 17449c87f76SRyan Roberts return pte; 175d8bdcff2SSteve Capper } 176d8bdcff2SSteve Capper 17741098230SWill Deacon static pte_t get_clear_contig_flush(struct mm_struct *mm, 17841098230SWill Deacon unsigned long addr, 17941098230SWill Deacon pte_t *ptep, 18041098230SWill Deacon unsigned long pgsize, 18141098230SWill Deacon unsigned long ncontig) 18241098230SWill Deacon { 18341098230SWill Deacon pte_t orig_pte = get_clear_contig(mm, addr, ptep, pgsize, ncontig); 18441098230SWill Deacon struct vm_area_struct vma = TLB_FLUSH_VMA(mm, 0); 1855b3f8917SRyan Roberts unsigned long end = addr + (pgsize * ncontig); 18641098230SWill Deacon 1875b3f8917SRyan Roberts __flush_hugetlb_tlb_range(&vma, addr, end, pgsize, true); 18841098230SWill Deacon return orig_pte; 18941098230SWill Deacon } 19041098230SWill Deacon 191d8bdcff2SSteve Capper /* 192d8bdcff2SSteve Capper * Changing some bits of contiguous entries requires us to follow a 193d8bdcff2SSteve Capper * Break-Before-Make approach, breaking the whole contiguous set 194d8bdcff2SSteve Capper * before we can change any entries. See ARM DDI 0487A.k_iss10775, 195d8bdcff2SSteve Capper * "Misprogramming of the Contiguous bit", page D4-1762. 196d8bdcff2SSteve Capper * 197d8bdcff2SSteve Capper * This helper performs the break step for use cases where the 198d8bdcff2SSteve Capper * original pte is not needed. 199d8bdcff2SSteve Capper */ 200d8bdcff2SSteve Capper static void clear_flush(struct mm_struct *mm, 201d8bdcff2SSteve Capper unsigned long addr, 202d8bdcff2SSteve Capper pte_t *ptep, 203d8bdcff2SSteve Capper unsigned long pgsize, 204d8bdcff2SSteve Capper unsigned long ncontig) 205d8bdcff2SSteve Capper { 2068b11ec1bSLinus Torvalds struct vm_area_struct vma = TLB_FLUSH_VMA(mm, 0); 207d8bdcff2SSteve Capper unsigned long i, saddr = addr; 208d8bdcff2SSteve Capper 209d8bdcff2SSteve Capper for (i = 0; i < ncontig; i++, addr += pgsize, ptep++) 210*a899b7d0SRyan Roberts __ptep_get_and_clear_anysz(mm, ptep, pgsize); 211d8bdcff2SSteve Capper 2125b3f8917SRyan Roberts __flush_hugetlb_tlb_range(&vma, saddr, addr, pgsize, true); 213d8bdcff2SSteve Capper } 214d8bdcff2SSteve Capper 21566b3923aSDavid Woods void set_huge_pte_at(struct mm_struct *mm, unsigned long addr, 216935d4f0cSRyan Roberts pte_t *ptep, pte_t pte, unsigned long sz) 21766b3923aSDavid Woods { 21866b3923aSDavid Woods size_t pgsize; 21966b3923aSDavid Woods int i; 220bb9dd3dfSSteve Capper int ncontig; 22166b3923aSDavid Woods 2226f1bace9SRyan Roberts ncontig = num_contig_ptes(sz, &pgsize); 2236f1bace9SRyan Roberts 22418f39629SQi Zheng if (!pte_present(pte)) { 2256f1bace9SRyan Roberts for (i = 0; i < ncontig; i++, ptep++, addr += pgsize) 226*a899b7d0SRyan Roberts __set_ptes_anysz(mm, ptep, pte, 1, pgsize); 22718f39629SQi Zheng return; 22818f39629SQi Zheng } 229d3ea7952SSteve Capper 2305b3f8917SRyan Roberts /* Only need to "break" if transitioning valid -> valid. */ 231*a899b7d0SRyan Roberts if (pte_cont(pte) && pte_valid(__ptep_get(ptep))) 232d8bdcff2SSteve Capper clear_flush(mm, addr, ptep, pgsize, ncontig); 233d8bdcff2SSteve Capper 234*a899b7d0SRyan Roberts __set_ptes_anysz(mm, ptep, pte, ncontig, pgsize); 23566b3923aSDavid Woods } 23666b3923aSDavid Woods 237aec44e0fSPeter Xu pte_t *huge_pte_alloc(struct mm_struct *mm, struct vm_area_struct *vma, 23866b3923aSDavid Woods unsigned long addr, unsigned long sz) 23966b3923aSDavid Woods { 24020a004e7SWill Deacon pgd_t *pgdp; 241e9f63768SMike Rapoport p4d_t *p4dp; 24220a004e7SWill Deacon pud_t *pudp; 24320a004e7SWill Deacon pmd_t *pmdp; 24420a004e7SWill Deacon pte_t *ptep = NULL; 24566b3923aSDavid Woods 24620a004e7SWill Deacon pgdp = pgd_offset(mm, addr); 247015a12a4SAnshuman Khandual p4dp = p4d_alloc(mm, pgdp, addr); 248015a12a4SAnshuman Khandual if (!p4dp) 249015a12a4SAnshuman Khandual return NULL; 250015a12a4SAnshuman Khandual 251e9f63768SMike Rapoport pudp = pud_alloc(mm, p4dp, addr); 25220a004e7SWill Deacon if (!pudp) 25366b3923aSDavid Woods return NULL; 25466b3923aSDavid Woods 25566b3923aSDavid Woods if (sz == PUD_SIZE) { 25620a004e7SWill Deacon ptep = (pte_t *)pudp; 257441a6278SAnshuman Khandual } else if (sz == (CONT_PTE_SIZE)) { 25820a004e7SWill Deacon pmdp = pmd_alloc(mm, pudp, addr); 259027d0c71SMark Rutland if (!pmdp) 260027d0c71SMark Rutland return NULL; 26166b3923aSDavid Woods 26266b3923aSDavid Woods WARN_ON(addr & (sz - 1)); 263cafcb9caSHugh Dickins ptep = pte_alloc_huge(mm, pmdp, addr); 26466b3923aSDavid Woods } else if (sz == PMD_SIZE) { 265c1991e07SPeter Xu if (want_pmd_share(vma, addr) && pud_none(READ_ONCE(*pudp))) 266aec44e0fSPeter Xu ptep = huge_pmd_share(mm, vma, addr, pudp); 26766b3923aSDavid Woods else 26820a004e7SWill Deacon ptep = (pte_t *)pmd_alloc(mm, pudp, addr); 269441a6278SAnshuman Khandual } else if (sz == (CONT_PMD_SIZE)) { 27020a004e7SWill Deacon pmdp = pmd_alloc(mm, pudp, addr); 27166b3923aSDavid Woods WARN_ON(addr & (sz - 1)); 27220a004e7SWill Deacon return (pte_t *)pmdp; 27366b3923aSDavid Woods } 27466b3923aSDavid Woods 27520a004e7SWill Deacon return ptep; 27666b3923aSDavid Woods } 27766b3923aSDavid Woods 2787868a208SPunit Agrawal pte_t *huge_pte_offset(struct mm_struct *mm, 2797868a208SPunit Agrawal unsigned long addr, unsigned long sz) 28066b3923aSDavid Woods { 28120a004e7SWill Deacon pgd_t *pgdp; 282e9f63768SMike Rapoport p4d_t *p4dp; 28320a004e7SWill Deacon pud_t *pudp, pud; 28420a004e7SWill Deacon pmd_t *pmdp, pmd; 28566b3923aSDavid Woods 28620a004e7SWill Deacon pgdp = pgd_offset(mm, addr); 28720a004e7SWill Deacon if (!pgd_present(READ_ONCE(*pgdp))) 28866b3923aSDavid Woods return NULL; 289f02ab08aSPunit Agrawal 290e9f63768SMike Rapoport p4dp = p4d_offset(pgdp, addr); 291e9f63768SMike Rapoport if (!p4d_present(READ_ONCE(*p4dp))) 292e9f63768SMike Rapoport return NULL; 293e9f63768SMike Rapoport 294e9f63768SMike Rapoport pudp = pud_offset(p4dp, addr); 29520a004e7SWill Deacon pud = READ_ONCE(*pudp); 29620a004e7SWill Deacon if (sz != PUD_SIZE && pud_none(pud)) 29766b3923aSDavid Woods return NULL; 29830f3ac00SPunit Agrawal /* hugepage or swap? */ 2991965e933SPeter Xu if (pud_leaf(pud) || !pud_present(pud)) 30020a004e7SWill Deacon return (pte_t *)pudp; 301f02ab08aSPunit Agrawal /* table; check the next level */ 30266b3923aSDavid Woods 30330f3ac00SPunit Agrawal if (sz == CONT_PMD_SIZE) 30430f3ac00SPunit Agrawal addr &= CONT_PMD_MASK; 30530f3ac00SPunit Agrawal 30620a004e7SWill Deacon pmdp = pmd_offset(pudp, addr); 30720a004e7SWill Deacon pmd = READ_ONCE(*pmdp); 30830f3ac00SPunit Agrawal if (!(sz == PMD_SIZE || sz == CONT_PMD_SIZE) && 30920a004e7SWill Deacon pmd_none(pmd)) 310f02ab08aSPunit Agrawal return NULL; 3111965e933SPeter Xu if (pmd_leaf(pmd) || !pmd_present(pmd)) 31220a004e7SWill Deacon return (pte_t *)pmdp; 313f02ab08aSPunit Agrawal 31420a004e7SWill Deacon if (sz == CONT_PTE_SIZE) 315cafcb9caSHugh Dickins return pte_offset_huge(pmdp, (addr & CONT_PTE_MASK)); 31630f3ac00SPunit Agrawal 31766b3923aSDavid Woods return NULL; 31866b3923aSDavid Woods } 31966b3923aSDavid Woods 3201bcdb769SBaolin Wang unsigned long hugetlb_mask_last_page(struct hstate *h) 3211bcdb769SBaolin Wang { 3221bcdb769SBaolin Wang unsigned long hp_size = huge_page_size(h); 3231bcdb769SBaolin Wang 3241bcdb769SBaolin Wang switch (hp_size) { 3251bcdb769SBaolin Wang #ifndef __PAGETABLE_PMD_FOLDED 3261bcdb769SBaolin Wang case PUD_SIZE: 32734e8e63aSAnshuman Khandual if (pud_sect_supported()) 3281bcdb769SBaolin Wang return PGDIR_SIZE - PUD_SIZE; 32934e8e63aSAnshuman Khandual break; 3301bcdb769SBaolin Wang #endif 3311bcdb769SBaolin Wang case CONT_PMD_SIZE: 3321bcdb769SBaolin Wang return PUD_SIZE - CONT_PMD_SIZE; 3331bcdb769SBaolin Wang case PMD_SIZE: 3341bcdb769SBaolin Wang return PUD_SIZE - PMD_SIZE; 3351bcdb769SBaolin Wang case CONT_PTE_SIZE: 3361bcdb769SBaolin Wang return PMD_SIZE - CONT_PTE_SIZE; 3371bcdb769SBaolin Wang default: 3381bcdb769SBaolin Wang break; 3391bcdb769SBaolin Wang } 3401bcdb769SBaolin Wang 3411bcdb769SBaolin Wang return 0UL; 3421bcdb769SBaolin Wang } 3431bcdb769SBaolin Wang 34479c1c594SChristophe Leroy pte_t arch_make_huge_pte(pte_t entry, unsigned int shift, vm_flags_t flags) 34566b3923aSDavid Woods { 34679c1c594SChristophe Leroy size_t pagesize = 1UL << shift; 34766b3923aSDavid Woods 348f8192813SAnshuman Khandual switch (pagesize) { 349f8192813SAnshuman Khandual #ifndef __PAGETABLE_PMD_FOLDED 350f8192813SAnshuman Khandual case PUD_SIZE: 35134e8e63aSAnshuman Khandual if (pud_sect_supported()) 35234e8e63aSAnshuman Khandual return pud_pte(pud_mkhuge(pte_pud(entry))); 353f8192813SAnshuman Khandual break; 354f8192813SAnshuman Khandual #endif 355f8192813SAnshuman Khandual case CONT_PMD_SIZE: 35634e8e63aSAnshuman Khandual return pmd_pte(pmd_mkhuge(pmd_mkcont(pte_pmd(entry)))); 357f8192813SAnshuman Khandual case PMD_SIZE: 35834e8e63aSAnshuman Khandual return pmd_pte(pmd_mkhuge(pte_pmd(entry))); 359f8192813SAnshuman Khandual case CONT_PTE_SIZE: 36034e8e63aSAnshuman Khandual return pte_mkcont(entry); 361f8192813SAnshuman Khandual default: 362f8192813SAnshuman Khandual break; 36366b3923aSDavid Woods } 36434e8e63aSAnshuman Khandual pr_warn("%s: unrecognized huge page size 0x%lx\n", 36534e8e63aSAnshuman Khandual __func__, pagesize); 36666b3923aSDavid Woods return entry; 36766b3923aSDavid Woods } 36866b3923aSDavid Woods 369c3e4ed5cSPunit Agrawal void huge_pte_clear(struct mm_struct *mm, unsigned long addr, 370c3e4ed5cSPunit Agrawal pte_t *ptep, unsigned long sz) 371c3e4ed5cSPunit Agrawal { 372c3e4ed5cSPunit Agrawal int i, ncontig; 373c3e4ed5cSPunit Agrawal size_t pgsize; 374c3e4ed5cSPunit Agrawal 375c3e4ed5cSPunit Agrawal ncontig = num_contig_ptes(sz, &pgsize); 376c3e4ed5cSPunit Agrawal 377c3e4ed5cSPunit Agrawal for (i = 0; i < ncontig; i++, addr += pgsize, ptep++) 3785a00bfd6SRyan Roberts __pte_clear(mm, addr, ptep); 379c3e4ed5cSPunit Agrawal } 380c3e4ed5cSPunit Agrawal 38102410ac7SRyan Roberts pte_t huge_ptep_get_and_clear(struct mm_struct *mm, unsigned long addr, 38202410ac7SRyan Roberts pte_t *ptep, unsigned long sz) 38366b3923aSDavid Woods { 384d8bdcff2SSteve Capper int ncontig; 38566b3923aSDavid Woods size_t pgsize; 38629a7287dSSteve Capper 38749c87f76SRyan Roberts ncontig = num_contig_ptes(sz, &pgsize); 388fb396bb4SAnshuman Khandual return get_clear_contig(mm, addr, ptep, pgsize, ncontig); 38966b3923aSDavid Woods } 39066b3923aSDavid Woods 391031e6e6bSSteve Capper /* 392031e6e6bSSteve Capper * huge_ptep_set_access_flags will update access flags (dirty, accesssed) 393031e6e6bSSteve Capper * and write permission. 394031e6e6bSSteve Capper * 395031e6e6bSSteve Capper * For a contiguous huge pte range we need to check whether or not write 396031e6e6bSSteve Capper * permission has to change only on the first pte in the set. Then for 397031e6e6bSSteve Capper * all the contiguous ptes we need to check whether or not there is a 398031e6e6bSSteve Capper * discrepancy between dirty or young. 399031e6e6bSSteve Capper */ 400031e6e6bSSteve Capper static int __cont_access_flags_changed(pte_t *ptep, pte_t pte, int ncontig) 401031e6e6bSSteve Capper { 402031e6e6bSSteve Capper int i; 403031e6e6bSSteve Capper 4045a00bfd6SRyan Roberts if (pte_write(pte) != pte_write(__ptep_get(ptep))) 405031e6e6bSSteve Capper return 1; 406031e6e6bSSteve Capper 407031e6e6bSSteve Capper for (i = 0; i < ncontig; i++) { 4085a00bfd6SRyan Roberts pte_t orig_pte = __ptep_get(ptep + i); 409031e6e6bSSteve Capper 410031e6e6bSSteve Capper if (pte_dirty(pte) != pte_dirty(orig_pte)) 411031e6e6bSSteve Capper return 1; 412031e6e6bSSteve Capper 413031e6e6bSSteve Capper if (pte_young(pte) != pte_young(orig_pte)) 414031e6e6bSSteve Capper return 1; 415031e6e6bSSteve Capper } 416031e6e6bSSteve Capper 417031e6e6bSSteve Capper return 0; 418031e6e6bSSteve Capper } 419031e6e6bSSteve Capper 42066b3923aSDavid Woods int huge_ptep_set_access_flags(struct vm_area_struct *vma, 42166b3923aSDavid Woods unsigned long addr, pte_t *ptep, 42266b3923aSDavid Woods pte_t pte, int dirty) 42366b3923aSDavid Woods { 424*a899b7d0SRyan Roberts int ncontig; 42566b3923aSDavid Woods size_t pgsize = 0; 42641098230SWill Deacon struct mm_struct *mm = vma->vm_mm; 427d8bdcff2SSteve Capper pte_t orig_pte; 42866b3923aSDavid Woods 42929cb8051SRyan Roberts VM_WARN_ON(!pte_present(pte)); 43029cb8051SRyan Roberts 43129a7287dSSteve Capper if (!pte_cont(pte)) 4325a00bfd6SRyan Roberts return __ptep_set_access_flags(vma, addr, ptep, pte, dirty); 43329a7287dSSteve Capper 43429cb8051SRyan Roberts ncontig = num_contig_ptes(huge_page_size(hstate_vma(vma)), &pgsize); 43529a7287dSSteve Capper 436031e6e6bSSteve Capper if (!__cont_access_flags_changed(ptep, pte, ncontig)) 437031e6e6bSSteve Capper return 0; 438031e6e6bSSteve Capper 43941098230SWill Deacon orig_pte = get_clear_contig_flush(mm, addr, ptep, pgsize, ncontig); 44029cb8051SRyan Roberts VM_WARN_ON(!pte_present(orig_pte)); 441d8bdcff2SSteve Capper 442469ed9d8SSteve Capper /* Make sure we don't lose the dirty or young state */ 443d8bdcff2SSteve Capper if (pte_dirty(orig_pte)) 444d8bdcff2SSteve Capper pte = pte_mkdirty(pte); 445d8bdcff2SSteve Capper 446469ed9d8SSteve Capper if (pte_young(orig_pte)) 447469ed9d8SSteve Capper pte = pte_mkyoung(pte); 448469ed9d8SSteve Capper 449*a899b7d0SRyan Roberts __set_ptes_anysz(mm, ptep, pte, ncontig, pgsize); 450031e6e6bSSteve Capper return 1; 45166b3923aSDavid Woods } 45266b3923aSDavid Woods 45366b3923aSDavid Woods void huge_ptep_set_wrprotect(struct mm_struct *mm, 45466b3923aSDavid Woods unsigned long addr, pte_t *ptep) 45566b3923aSDavid Woods { 456*a899b7d0SRyan Roberts int ncontig; 45729a7287dSSteve Capper size_t pgsize; 458d8bdcff2SSteve Capper pte_t pte; 45929a7287dSSteve Capper 46029cb8051SRyan Roberts pte = __ptep_get(ptep); 46129cb8051SRyan Roberts VM_WARN_ON(!pte_present(pte)); 46229cb8051SRyan Roberts 46329cb8051SRyan Roberts if (!pte_cont(pte)) { 4645a00bfd6SRyan Roberts __ptep_set_wrprotect(mm, addr, ptep); 46529a7287dSSteve Capper return; 46629a7287dSSteve Capper } 46766b3923aSDavid Woods 468f0b38d65SSteve Capper ncontig = find_num_contig(mm, addr, ptep, &pgsize); 469d8bdcff2SSteve Capper 47041098230SWill Deacon pte = get_clear_contig_flush(mm, addr, ptep, pgsize, ncontig); 471d8bdcff2SSteve Capper pte = pte_wrprotect(pte); 472d8bdcff2SSteve Capper 473*a899b7d0SRyan Roberts __set_ptes_anysz(mm, ptep, pte, ncontig, pgsize); 47466b3923aSDavid Woods } 47566b3923aSDavid Woods 476ae075629SBaolin Wang pte_t huge_ptep_clear_flush(struct vm_area_struct *vma, 47766b3923aSDavid Woods unsigned long addr, pte_t *ptep) 47866b3923aSDavid Woods { 47941098230SWill Deacon struct mm_struct *mm = vma->vm_mm; 48029a7287dSSteve Capper size_t pgsize; 481d8bdcff2SSteve Capper int ncontig; 48229a7287dSSteve Capper 48329cb8051SRyan Roberts ncontig = num_contig_ptes(huge_page_size(hstate_vma(vma)), &pgsize); 48441098230SWill Deacon return get_clear_contig_flush(mm, addr, ptep, pgsize, ncontig); 48566b3923aSDavid Woods } 48666b3923aSDavid Woods 487a21b0b78SAllen Pais static int __init hugetlbpage_init(void) 488a21b0b78SAllen Pais { 4891e5823c8SAnshuman Khandual /* 4901e5823c8SAnshuman Khandual * HugeTLB pages are supported on maximum four page table 4911e5823c8SAnshuman Khandual * levels (PUD, CONT PMD, PMD, CONT PTE) for a given base 4921e5823c8SAnshuman Khandual * page size, corresponding to hugetlb_add_hstate() calls 4931e5823c8SAnshuman Khandual * here. 4941e5823c8SAnshuman Khandual * 4951e5823c8SAnshuman Khandual * HUGE_MAX_HSTATE should at least match maximum supported 4961e5823c8SAnshuman Khandual * HugeTLB page sizes on the platform. Any new addition to 4971e5823c8SAnshuman Khandual * supported HugeTLB page sizes will also require changing 4981e5823c8SAnshuman Khandual * HUGE_MAX_HSTATE as well. 4991e5823c8SAnshuman Khandual */ 5001e5823c8SAnshuman Khandual BUILD_BUG_ON(HUGE_MAX_HSTATE < 4); 501f8b46c4bSAnshuman Khandual if (pud_sect_supported()) 50238237830SMike Kravetz hugetlb_add_hstate(PUD_SHIFT - PAGE_SHIFT); 503f8b46c4bSAnshuman Khandual 504a1634a54SGavin Shan hugetlb_add_hstate(CONT_PMD_SHIFT - PAGE_SHIFT); 50538237830SMike Kravetz hugetlb_add_hstate(PMD_SHIFT - PAGE_SHIFT); 506a1634a54SGavin Shan hugetlb_add_hstate(CONT_PTE_SHIFT - PAGE_SHIFT); 507a21b0b78SAllen Pais 508a21b0b78SAllen Pais return 0; 509a21b0b78SAllen Pais } 510a21b0b78SAllen Pais arch_initcall(hugetlbpage_init); 511a21b0b78SAllen Pais 512ae94da89SMike Kravetz bool __init arch_hugetlb_valid_size(unsigned long size) 513084bd298SSteve Capper { 514a8a733b2SAnshuman Khandual return __hugetlb_valid_size(size); 515ae94da89SMike Kravetz } 5165db568e7SAnshuman Khandual 5175db568e7SAnshuman Khandual pte_t huge_ptep_modify_prot_start(struct vm_area_struct *vma, unsigned long addr, pte_t *ptep) 5185db568e7SAnshuman Khandual { 51902410ac7SRyan Roberts unsigned long psize = huge_page_size(hstate_vma(vma)); 52002410ac7SRyan Roberts 521412cb380SMark Rutland if (alternative_has_cap_unlikely(ARM64_WORKAROUND_2645198)) { 5225db568e7SAnshuman Khandual /* 5235db568e7SAnshuman Khandual * Break-before-make (BBM) is required for all user space mappings 5245db568e7SAnshuman Khandual * when the permission changes from executable to non-executable 5255db568e7SAnshuman Khandual * in cases where cpu is affected with errata #2645198. 5265db568e7SAnshuman Khandual */ 5275a00bfd6SRyan Roberts if (pte_user_exec(__ptep_get(ptep))) 5285db568e7SAnshuman Khandual return huge_ptep_clear_flush(vma, addr, ptep); 5295db568e7SAnshuman Khandual } 53002410ac7SRyan Roberts return huge_ptep_get_and_clear(vma->vm_mm, addr, ptep, psize); 5315db568e7SAnshuman Khandual } 5325db568e7SAnshuman Khandual 5335db568e7SAnshuman Khandual void huge_ptep_modify_prot_commit(struct vm_area_struct *vma, unsigned long addr, pte_t *ptep, 5345db568e7SAnshuman Khandual pte_t old_pte, pte_t pte) 5355db568e7SAnshuman Khandual { 536935d4f0cSRyan Roberts unsigned long psize = huge_page_size(hstate_vma(vma)); 537935d4f0cSRyan Roberts 538935d4f0cSRyan Roberts set_huge_pte_at(vma->vm_mm, addr, ptep, pte, psize); 5395db568e7SAnshuman Khandual } 540