xref: /linux/arch/arm64/mm/hugetlbpage.c (revision 20a004e7b017cce282a46ac5d02c2b9c6b9bb1fa)
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