1084bd298SSteve Capper /* 2084bd298SSteve Capper * arch/arm64/mm/hugetlbpage.c 3084bd298SSteve Capper * 4084bd298SSteve Capper * Copyright (C) 2013 Linaro Ltd. 5084bd298SSteve Capper * 6084bd298SSteve Capper * Based on arch/x86/mm/hugetlbpage.c. 7084bd298SSteve Capper * 8084bd298SSteve Capper * This program is free software; you can redistribute it and/or modify 9084bd298SSteve Capper * it under the terms of the GNU General Public License version 2 as 10084bd298SSteve Capper * published by the Free Software Foundation. 11084bd298SSteve Capper * 12084bd298SSteve Capper * This program is distributed in the hope that it will be useful, 13084bd298SSteve Capper * but WITHOUT ANY WARRANTY; without even the implied warranty of 14084bd298SSteve Capper * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15084bd298SSteve Capper * GNU General Public License for more details. 16084bd298SSteve Capper */ 17084bd298SSteve Capper 18084bd298SSteve Capper #include <linux/init.h> 19084bd298SSteve Capper #include <linux/fs.h> 20084bd298SSteve Capper #include <linux/mm.h> 21084bd298SSteve Capper #include <linux/hugetlb.h> 22084bd298SSteve Capper #include <linux/pagemap.h> 23084bd298SSteve Capper #include <linux/err.h> 24084bd298SSteve Capper #include <linux/sysctl.h> 25084bd298SSteve Capper #include <asm/mman.h> 26084bd298SSteve Capper #include <asm/tlb.h> 27084bd298SSteve Capper #include <asm/tlbflush.h> 28084bd298SSteve Capper #include <asm/pgalloc.h> 29084bd298SSteve Capper 30084bd298SSteve Capper int pmd_huge(pmd_t pmd) 31084bd298SSteve Capper { 32fd28f5d4SChristoffer Dall return pmd_val(pmd) && !(pmd_val(pmd) & PMD_TABLE_BIT); 33084bd298SSteve Capper } 34084bd298SSteve Capper 35084bd298SSteve Capper int pud_huge(pud_t pud) 36084bd298SSteve Capper { 374797ec2dSMark Salter #ifndef __PAGETABLE_PMD_FOLDED 38fd28f5d4SChristoffer Dall return pud_val(pud) && !(pud_val(pud) & PUD_TABLE_BIT); 394797ec2dSMark Salter #else 404797ec2dSMark Salter return 0; 414797ec2dSMark Salter #endif 42084bd298SSteve Capper } 43084bd298SSteve Capper 44b5b0be86SSteve Capper /* 45b5b0be86SSteve Capper * Select all bits except the pfn 46b5b0be86SSteve Capper */ 47b5b0be86SSteve Capper static inline pgprot_t pte_pgprot(pte_t pte) 48b5b0be86SSteve Capper { 49b5b0be86SSteve Capper unsigned long pfn = pte_pfn(pte); 50b5b0be86SSteve Capper 51b5b0be86SSteve Capper return __pgprot(pte_val(pfn_pte(pfn, __pgprot(0))) ^ pte_val(pte)); 52b5b0be86SSteve Capper } 53b5b0be86SSteve Capper 5466b3923aSDavid Woods static int find_num_contig(struct mm_struct *mm, unsigned long addr, 55bb9dd3dfSSteve Capper pte_t *ptep, size_t *pgsize) 5666b3923aSDavid Woods { 57*20a004e7SWill Deacon pgd_t *pgdp = pgd_offset(mm, addr); 58*20a004e7SWill Deacon pud_t *pudp; 59*20a004e7SWill Deacon pmd_t *pmdp; 6066b3923aSDavid Woods 6166b3923aSDavid Woods *pgsize = PAGE_SIZE; 62*20a004e7SWill Deacon pudp = pud_offset(pgdp, addr); 63*20a004e7SWill Deacon pmdp = pmd_offset(pudp, addr); 64*20a004e7SWill Deacon if ((pte_t *)pmdp == ptep) { 6566b3923aSDavid Woods *pgsize = PMD_SIZE; 6666b3923aSDavid Woods return CONT_PMDS; 6766b3923aSDavid Woods } 6866b3923aSDavid Woods return CONT_PTES; 6966b3923aSDavid Woods } 7066b3923aSDavid Woods 71c3e4ed5cSPunit Agrawal static inline int num_contig_ptes(unsigned long size, size_t *pgsize) 72c3e4ed5cSPunit Agrawal { 73c3e4ed5cSPunit Agrawal int contig_ptes = 0; 74c3e4ed5cSPunit Agrawal 75c3e4ed5cSPunit Agrawal *pgsize = size; 76c3e4ed5cSPunit Agrawal 77c3e4ed5cSPunit Agrawal switch (size) { 78c3e4ed5cSPunit Agrawal #ifdef CONFIG_ARM64_4K_PAGES 79c3e4ed5cSPunit Agrawal case PUD_SIZE: 80c3e4ed5cSPunit Agrawal #endif 81c3e4ed5cSPunit Agrawal case PMD_SIZE: 82c3e4ed5cSPunit Agrawal contig_ptes = 1; 83c3e4ed5cSPunit Agrawal break; 84c3e4ed5cSPunit Agrawal case CONT_PMD_SIZE: 85c3e4ed5cSPunit Agrawal *pgsize = PMD_SIZE; 86c3e4ed5cSPunit Agrawal contig_ptes = CONT_PMDS; 87c3e4ed5cSPunit Agrawal break; 88c3e4ed5cSPunit Agrawal case CONT_PTE_SIZE: 89c3e4ed5cSPunit Agrawal *pgsize = PAGE_SIZE; 90c3e4ed5cSPunit Agrawal contig_ptes = CONT_PTES; 91c3e4ed5cSPunit Agrawal break; 92c3e4ed5cSPunit Agrawal } 93c3e4ed5cSPunit Agrawal 94c3e4ed5cSPunit Agrawal return contig_ptes; 95c3e4ed5cSPunit Agrawal } 96c3e4ed5cSPunit Agrawal 97d8bdcff2SSteve Capper /* 98d8bdcff2SSteve Capper * Changing some bits of contiguous entries requires us to follow a 99d8bdcff2SSteve Capper * Break-Before-Make approach, breaking the whole contiguous set 100d8bdcff2SSteve Capper * before we can change any entries. See ARM DDI 0487A.k_iss10775, 101d8bdcff2SSteve Capper * "Misprogramming of the Contiguous bit", page D4-1762. 102d8bdcff2SSteve Capper * 103d8bdcff2SSteve Capper * This helper performs the break step. 104d8bdcff2SSteve Capper */ 105d8bdcff2SSteve Capper static pte_t get_clear_flush(struct mm_struct *mm, 106d8bdcff2SSteve Capper unsigned long addr, 107d8bdcff2SSteve Capper pte_t *ptep, 108d8bdcff2SSteve Capper unsigned long pgsize, 109d8bdcff2SSteve Capper unsigned long ncontig) 110d8bdcff2SSteve Capper { 111d8bdcff2SSteve Capper struct vm_area_struct vma = { .vm_mm = mm }; 112d8bdcff2SSteve Capper pte_t orig_pte = huge_ptep_get(ptep); 113d8bdcff2SSteve Capper bool valid = pte_valid(orig_pte); 114d8bdcff2SSteve Capper unsigned long i, saddr = addr; 115d8bdcff2SSteve Capper 116d8bdcff2SSteve Capper for (i = 0; i < ncontig; i++, addr += pgsize, ptep++) { 117d8bdcff2SSteve Capper pte_t pte = ptep_get_and_clear(mm, addr, ptep); 118d8bdcff2SSteve Capper 119d8bdcff2SSteve Capper /* 120d8bdcff2SSteve Capper * If HW_AFDBM is enabled, then the HW could turn on 121d8bdcff2SSteve Capper * the dirty bit for any page in the set, so check 122d8bdcff2SSteve Capper * them all. All hugetlb entries are already young. 123d8bdcff2SSteve Capper */ 124d8bdcff2SSteve Capper if (pte_dirty(pte)) 125d8bdcff2SSteve Capper orig_pte = pte_mkdirty(orig_pte); 126d8bdcff2SSteve Capper } 127d8bdcff2SSteve Capper 128d8bdcff2SSteve Capper if (valid) 129d8bdcff2SSteve Capper flush_tlb_range(&vma, saddr, addr); 130d8bdcff2SSteve Capper return orig_pte; 131d8bdcff2SSteve Capper } 132d8bdcff2SSteve Capper 133d8bdcff2SSteve Capper /* 134d8bdcff2SSteve Capper * Changing some bits of contiguous entries requires us to follow a 135d8bdcff2SSteve Capper * Break-Before-Make approach, breaking the whole contiguous set 136d8bdcff2SSteve Capper * before we can change any entries. See ARM DDI 0487A.k_iss10775, 137d8bdcff2SSteve Capper * "Misprogramming of the Contiguous bit", page D4-1762. 138d8bdcff2SSteve Capper * 139d8bdcff2SSteve Capper * This helper performs the break step for use cases where the 140d8bdcff2SSteve Capper * original pte is not needed. 141d8bdcff2SSteve Capper */ 142d8bdcff2SSteve Capper static void clear_flush(struct mm_struct *mm, 143d8bdcff2SSteve Capper unsigned long addr, 144d8bdcff2SSteve Capper pte_t *ptep, 145d8bdcff2SSteve Capper unsigned long pgsize, 146d8bdcff2SSteve Capper unsigned long ncontig) 147d8bdcff2SSteve Capper { 148d8bdcff2SSteve Capper struct vm_area_struct vma = { .vm_mm = mm }; 149d8bdcff2SSteve Capper unsigned long i, saddr = addr; 150d8bdcff2SSteve Capper 151d8bdcff2SSteve Capper for (i = 0; i < ncontig; i++, addr += pgsize, ptep++) 152d8bdcff2SSteve Capper pte_clear(mm, addr, ptep); 153d8bdcff2SSteve Capper 154d8bdcff2SSteve Capper flush_tlb_range(&vma, saddr, addr); 155d8bdcff2SSteve Capper } 156d8bdcff2SSteve Capper 15766b3923aSDavid Woods void set_huge_pte_at(struct mm_struct *mm, unsigned long addr, 15866b3923aSDavid Woods pte_t *ptep, pte_t pte) 15966b3923aSDavid Woods { 16066b3923aSDavid Woods size_t pgsize; 16166b3923aSDavid Woods int i; 162bb9dd3dfSSteve Capper int ncontig; 16329a7287dSSteve Capper unsigned long pfn, dpfn; 16466b3923aSDavid Woods pgprot_t hugeprot; 16566b3923aSDavid Woods 166d3ea7952SSteve Capper /* 167d3ea7952SSteve Capper * Code needs to be expanded to handle huge swap and migration 168d3ea7952SSteve Capper * entries. Needed for HUGETLB and MEMORY_FAILURE. 169d3ea7952SSteve Capper */ 170d3ea7952SSteve Capper WARN_ON(!pte_present(pte)); 171d3ea7952SSteve Capper 172bb9dd3dfSSteve Capper if (!pte_cont(pte)) { 17366b3923aSDavid Woods set_pte_at(mm, addr, ptep, pte); 17466b3923aSDavid Woods return; 17566b3923aSDavid Woods } 17666b3923aSDavid Woods 177bb9dd3dfSSteve Capper ncontig = find_num_contig(mm, addr, ptep, &pgsize); 17866b3923aSDavid Woods pfn = pte_pfn(pte); 17929a7287dSSteve Capper dpfn = pgsize >> PAGE_SHIFT; 180b5b0be86SSteve Capper hugeprot = pte_pgprot(pte); 18129a7287dSSteve Capper 182d8bdcff2SSteve Capper clear_flush(mm, addr, ptep, pgsize, ncontig); 183d8bdcff2SSteve Capper 184*20a004e7SWill Deacon for (i = 0; i < ncontig; i++, ptep++, addr += pgsize, pfn += dpfn) 18566b3923aSDavid Woods set_pte_at(mm, addr, ptep, pfn_pte(pfn, hugeprot)); 18666b3923aSDavid Woods } 18766b3923aSDavid Woods 188a8d623eeSPunit Agrawal void set_huge_swap_pte_at(struct mm_struct *mm, unsigned long addr, 189a8d623eeSPunit Agrawal pte_t *ptep, pte_t pte, unsigned long sz) 190a8d623eeSPunit Agrawal { 191a8d623eeSPunit Agrawal int i, ncontig; 192a8d623eeSPunit Agrawal size_t pgsize; 193a8d623eeSPunit Agrawal 194a8d623eeSPunit Agrawal ncontig = num_contig_ptes(sz, &pgsize); 195a8d623eeSPunit Agrawal 196a8d623eeSPunit Agrawal for (i = 0; i < ncontig; i++, ptep++) 197a8d623eeSPunit Agrawal set_pte(ptep, pte); 198a8d623eeSPunit Agrawal } 199a8d623eeSPunit Agrawal 20066b3923aSDavid Woods pte_t *huge_pte_alloc(struct mm_struct *mm, 20166b3923aSDavid Woods unsigned long addr, unsigned long sz) 20266b3923aSDavid Woods { 203*20a004e7SWill Deacon pgd_t *pgdp; 204*20a004e7SWill Deacon pud_t *pudp; 205*20a004e7SWill Deacon pmd_t *pmdp; 206*20a004e7SWill Deacon pte_t *ptep = NULL; 20766b3923aSDavid Woods 208*20a004e7SWill Deacon pgdp = pgd_offset(mm, addr); 209*20a004e7SWill Deacon pudp = pud_alloc(mm, pgdp, addr); 210*20a004e7SWill Deacon if (!pudp) 21166b3923aSDavid Woods return NULL; 21266b3923aSDavid Woods 21366b3923aSDavid Woods if (sz == PUD_SIZE) { 214*20a004e7SWill Deacon ptep = (pte_t *)pudp; 21566b3923aSDavid Woods } else if (sz == (PAGE_SIZE * CONT_PTES)) { 216*20a004e7SWill Deacon pmdp = pmd_alloc(mm, pudp, addr); 21766b3923aSDavid Woods 21866b3923aSDavid Woods WARN_ON(addr & (sz - 1)); 21966b3923aSDavid Woods /* 22066b3923aSDavid Woods * Note that if this code were ever ported to the 22166b3923aSDavid Woods * 32-bit arm platform then it will cause trouble in 22266b3923aSDavid Woods * the case where CONFIG_HIGHPTE is set, since there 22366b3923aSDavid Woods * will be no pte_unmap() to correspond with this 22466b3923aSDavid Woods * pte_alloc_map(). 22566b3923aSDavid Woods */ 226*20a004e7SWill Deacon ptep = pte_alloc_map(mm, pmdp, addr); 22766b3923aSDavid Woods } else if (sz == PMD_SIZE) { 22866b3923aSDavid Woods if (IS_ENABLED(CONFIG_ARCH_WANT_HUGE_PMD_SHARE) && 229*20a004e7SWill Deacon pud_none(READ_ONCE(*pudp))) 230*20a004e7SWill Deacon ptep = huge_pmd_share(mm, addr, pudp); 23166b3923aSDavid Woods else 232*20a004e7SWill Deacon ptep = (pte_t *)pmd_alloc(mm, pudp, addr); 23366b3923aSDavid Woods } else if (sz == (PMD_SIZE * CONT_PMDS)) { 234*20a004e7SWill Deacon pmdp = pmd_alloc(mm, pudp, addr); 23566b3923aSDavid Woods WARN_ON(addr & (sz - 1)); 236*20a004e7SWill Deacon return (pte_t *)pmdp; 23766b3923aSDavid Woods } 23866b3923aSDavid Woods 239*20a004e7SWill Deacon return ptep; 24066b3923aSDavid Woods } 24166b3923aSDavid Woods 2427868a208SPunit Agrawal pte_t *huge_pte_offset(struct mm_struct *mm, 2437868a208SPunit Agrawal unsigned long addr, unsigned long sz) 24466b3923aSDavid Woods { 245*20a004e7SWill Deacon pgd_t *pgdp; 246*20a004e7SWill Deacon pud_t *pudp, pud; 247*20a004e7SWill Deacon pmd_t *pmdp, pmd; 24866b3923aSDavid Woods 249*20a004e7SWill Deacon pgdp = pgd_offset(mm, addr); 250*20a004e7SWill Deacon if (!pgd_present(READ_ONCE(*pgdp))) 25166b3923aSDavid Woods return NULL; 252f02ab08aSPunit Agrawal 253*20a004e7SWill Deacon pudp = pud_offset(pgdp, addr); 254*20a004e7SWill Deacon pud = READ_ONCE(*pudp); 255*20a004e7SWill Deacon if (sz != PUD_SIZE && pud_none(pud)) 25666b3923aSDavid Woods return NULL; 25730f3ac00SPunit Agrawal /* hugepage or swap? */ 258*20a004e7SWill Deacon if (pud_huge(pud) || !pud_present(pud)) 259*20a004e7SWill Deacon return (pte_t *)pudp; 260f02ab08aSPunit Agrawal /* table; check the next level */ 26166b3923aSDavid Woods 26230f3ac00SPunit Agrawal if (sz == CONT_PMD_SIZE) 26330f3ac00SPunit Agrawal addr &= CONT_PMD_MASK; 26430f3ac00SPunit Agrawal 265*20a004e7SWill Deacon pmdp = pmd_offset(pudp, addr); 266*20a004e7SWill Deacon pmd = READ_ONCE(*pmdp); 26730f3ac00SPunit Agrawal if (!(sz == PMD_SIZE || sz == CONT_PMD_SIZE) && 268*20a004e7SWill Deacon pmd_none(pmd)) 269f02ab08aSPunit Agrawal return NULL; 270*20a004e7SWill Deacon if (pmd_huge(pmd) || !pmd_present(pmd)) 271*20a004e7SWill Deacon return (pte_t *)pmdp; 272f02ab08aSPunit Agrawal 273*20a004e7SWill Deacon if (sz == CONT_PTE_SIZE) 274*20a004e7SWill Deacon return pte_offset_kernel(pmdp, (addr & CONT_PTE_MASK)); 27530f3ac00SPunit Agrawal 27666b3923aSDavid Woods return NULL; 27766b3923aSDavid Woods } 27866b3923aSDavid Woods 27966b3923aSDavid Woods pte_t arch_make_huge_pte(pte_t entry, struct vm_area_struct *vma, 28066b3923aSDavid Woods struct page *page, int writable) 28166b3923aSDavid Woods { 28266b3923aSDavid Woods size_t pagesize = huge_page_size(hstate_vma(vma)); 28366b3923aSDavid Woods 28466b3923aSDavid Woods if (pagesize == CONT_PTE_SIZE) { 28566b3923aSDavid Woods entry = pte_mkcont(entry); 28666b3923aSDavid Woods } else if (pagesize == CONT_PMD_SIZE) { 28766b3923aSDavid Woods entry = pmd_pte(pmd_mkcont(pte_pmd(entry))); 28866b3923aSDavid Woods } else if (pagesize != PUD_SIZE && pagesize != PMD_SIZE) { 28966b3923aSDavid Woods pr_warn("%s: unrecognized huge page size 0x%lx\n", 29066b3923aSDavid Woods __func__, pagesize); 29166b3923aSDavid Woods } 29266b3923aSDavid Woods return entry; 29366b3923aSDavid Woods } 29466b3923aSDavid Woods 295c3e4ed5cSPunit Agrawal void huge_pte_clear(struct mm_struct *mm, unsigned long addr, 296c3e4ed5cSPunit Agrawal pte_t *ptep, unsigned long sz) 297c3e4ed5cSPunit Agrawal { 298c3e4ed5cSPunit Agrawal int i, ncontig; 299c3e4ed5cSPunit Agrawal size_t pgsize; 300c3e4ed5cSPunit Agrawal 301c3e4ed5cSPunit Agrawal ncontig = num_contig_ptes(sz, &pgsize); 302c3e4ed5cSPunit Agrawal 303c3e4ed5cSPunit Agrawal for (i = 0; i < ncontig; i++, addr += pgsize, ptep++) 304c3e4ed5cSPunit Agrawal pte_clear(mm, addr, ptep); 305c3e4ed5cSPunit Agrawal } 306c3e4ed5cSPunit Agrawal 30766b3923aSDavid Woods pte_t huge_ptep_get_and_clear(struct mm_struct *mm, 30866b3923aSDavid Woods unsigned long addr, pte_t *ptep) 30966b3923aSDavid Woods { 310d8bdcff2SSteve Capper int ncontig; 31166b3923aSDavid Woods size_t pgsize; 31229a7287dSSteve Capper pte_t orig_pte = huge_ptep_get(ptep); 31329a7287dSSteve Capper 31429a7287dSSteve Capper if (!pte_cont(orig_pte)) 31529a7287dSSteve Capper return ptep_get_and_clear(mm, addr, ptep); 31666b3923aSDavid Woods 317f0b38d65SSteve Capper ncontig = find_num_contig(mm, addr, ptep, &pgsize); 31829a7287dSSteve Capper 319d8bdcff2SSteve Capper return get_clear_flush(mm, addr, ptep, pgsize, ncontig); 32066b3923aSDavid Woods } 32166b3923aSDavid Woods 32266b3923aSDavid Woods int huge_ptep_set_access_flags(struct vm_area_struct *vma, 32366b3923aSDavid Woods unsigned long addr, pte_t *ptep, 32466b3923aSDavid Woods pte_t pte, int dirty) 32566b3923aSDavid Woods { 32666b3923aSDavid Woods int ncontig, i, changed = 0; 32766b3923aSDavid Woods size_t pgsize = 0; 32829a7287dSSteve Capper unsigned long pfn = pte_pfn(pte), dpfn; 32929a7287dSSteve Capper pgprot_t hugeprot; 330d8bdcff2SSteve Capper pte_t orig_pte; 33166b3923aSDavid Woods 33229a7287dSSteve Capper if (!pte_cont(pte)) 33366b3923aSDavid Woods return ptep_set_access_flags(vma, addr, ptep, pte, dirty); 33429a7287dSSteve Capper 33529a7287dSSteve Capper ncontig = find_num_contig(vma->vm_mm, addr, ptep, &pgsize); 33629a7287dSSteve Capper dpfn = pgsize >> PAGE_SHIFT; 33729a7287dSSteve Capper 338d8bdcff2SSteve Capper orig_pte = get_clear_flush(vma->vm_mm, addr, ptep, pgsize, ncontig); 339d8bdcff2SSteve Capper if (!pte_same(orig_pte, pte)) 340d8bdcff2SSteve Capper changed = 1; 341d8bdcff2SSteve Capper 342d8bdcff2SSteve Capper /* Make sure we don't lose the dirty state */ 343d8bdcff2SSteve Capper if (pte_dirty(orig_pte)) 344d8bdcff2SSteve Capper pte = pte_mkdirty(pte); 345d8bdcff2SSteve Capper 346d8bdcff2SSteve Capper hugeprot = pte_pgprot(pte); 347d8bdcff2SSteve Capper for (i = 0; i < ncontig; i++, ptep++, addr += pgsize, pfn += dpfn) 348d8bdcff2SSteve Capper set_pte_at(vma->vm_mm, addr, ptep, pfn_pte(pfn, hugeprot)); 34929a7287dSSteve Capper 35029a7287dSSteve Capper return changed; 35166b3923aSDavid Woods } 35266b3923aSDavid Woods 35366b3923aSDavid Woods void huge_ptep_set_wrprotect(struct mm_struct *mm, 35466b3923aSDavid Woods unsigned long addr, pte_t *ptep) 35566b3923aSDavid Woods { 356d8bdcff2SSteve Capper unsigned long pfn, dpfn; 357d8bdcff2SSteve Capper pgprot_t hugeprot; 35866b3923aSDavid Woods int ncontig, i; 35929a7287dSSteve Capper size_t pgsize; 360d8bdcff2SSteve Capper pte_t pte; 36129a7287dSSteve Capper 362*20a004e7SWill Deacon if (!pte_cont(READ_ONCE(*ptep))) { 36329a7287dSSteve Capper ptep_set_wrprotect(mm, addr, ptep); 36429a7287dSSteve Capper return; 36529a7287dSSteve Capper } 36666b3923aSDavid Woods 367f0b38d65SSteve Capper ncontig = find_num_contig(mm, addr, ptep, &pgsize); 368d8bdcff2SSteve Capper dpfn = pgsize >> PAGE_SHIFT; 369d8bdcff2SSteve Capper 370d8bdcff2SSteve Capper pte = get_clear_flush(mm, addr, ptep, pgsize, ncontig); 371d8bdcff2SSteve Capper pte = pte_wrprotect(pte); 372d8bdcff2SSteve Capper 373d8bdcff2SSteve Capper hugeprot = pte_pgprot(pte); 374d8bdcff2SSteve Capper pfn = pte_pfn(pte); 375d8bdcff2SSteve Capper 376d8bdcff2SSteve Capper for (i = 0; i < ncontig; i++, ptep++, addr += pgsize, pfn += dpfn) 377d8bdcff2SSteve Capper set_pte_at(mm, addr, ptep, pfn_pte(pfn, hugeprot)); 37866b3923aSDavid Woods } 37966b3923aSDavid Woods 38066b3923aSDavid Woods void huge_ptep_clear_flush(struct vm_area_struct *vma, 38166b3923aSDavid Woods unsigned long addr, pte_t *ptep) 38266b3923aSDavid Woods { 38329a7287dSSteve Capper size_t pgsize; 384d8bdcff2SSteve Capper int ncontig; 38566b3923aSDavid Woods 386*20a004e7SWill Deacon if (!pte_cont(READ_ONCE(*ptep))) { 387f0b38d65SSteve Capper ptep_clear_flush(vma, addr, ptep); 38829a7287dSSteve Capper return; 38966b3923aSDavid Woods } 39029a7287dSSteve Capper 39129a7287dSSteve Capper ncontig = find_num_contig(vma->vm_mm, addr, ptep, &pgsize); 392d8bdcff2SSteve Capper clear_flush(vma->vm_mm, addr, ptep, pgsize, ncontig); 39366b3923aSDavid Woods } 39466b3923aSDavid Woods 395084bd298SSteve Capper static __init int setup_hugepagesz(char *opt) 396084bd298SSteve Capper { 397084bd298SSteve Capper unsigned long ps = memparse(opt, &opt); 39866b3923aSDavid Woods 399828f193dSSteve Capper switch (ps) { 400828f193dSSteve Capper #ifdef CONFIG_ARM64_4K_PAGES 401828f193dSSteve Capper case PUD_SIZE: 402828f193dSSteve Capper #endif 403828f193dSSteve Capper case PMD_SIZE * CONT_PMDS: 404828f193dSSteve Capper case PMD_SIZE: 405828f193dSSteve Capper case PAGE_SIZE * CONT_PTES: 406828f193dSSteve Capper hugetlb_add_hstate(ilog2(ps) - PAGE_SHIFT); 407828f193dSSteve Capper return 1; 408828f193dSSteve Capper } 409828f193dSSteve Capper 410d77e20ceSVaishali Thakkar hugetlb_bad_size(); 41166b3923aSDavid Woods pr_err("hugepagesz: Unsupported page size %lu K\n", ps >> 10); 412084bd298SSteve Capper return 0; 413084bd298SSteve Capper } 414084bd298SSteve Capper __setup("hugepagesz=", setup_hugepagesz); 4155cd028b9SPunit Agrawal 4165cd028b9SPunit Agrawal #ifdef CONFIG_ARM64_64K_PAGES 4175cd028b9SPunit Agrawal static __init int add_default_hugepagesz(void) 4185cd028b9SPunit Agrawal { 4195cd028b9SPunit Agrawal if (size_to_hstate(CONT_PTES * PAGE_SIZE) == NULL) 4205cd028b9SPunit Agrawal hugetlb_add_hstate(CONT_PTE_SHIFT); 4215cd028b9SPunit Agrawal return 0; 4225cd028b9SPunit Agrawal } 4235cd028b9SPunit Agrawal arch_initcall(add_default_hugepagesz); 4245cd028b9SPunit Agrawal #endif 425