18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * arch/arm64/mm/hugetlbpage.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2013 Linaro Ltd.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Based on arch/x86/mm/hugetlbpage.c.
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/init.h>
118c2ecf20Sopenharmony_ci#include <linux/fs.h>
128c2ecf20Sopenharmony_ci#include <linux/mm.h>
138c2ecf20Sopenharmony_ci#include <linux/hugetlb.h>
148c2ecf20Sopenharmony_ci#include <linux/pagemap.h>
158c2ecf20Sopenharmony_ci#include <linux/err.h>
168c2ecf20Sopenharmony_ci#include <linux/sysctl.h>
178c2ecf20Sopenharmony_ci#include <asm/mman.h>
188c2ecf20Sopenharmony_ci#include <asm/tlb.h>
198c2ecf20Sopenharmony_ci#include <asm/tlbflush.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci/*
228c2ecf20Sopenharmony_ci * HugeTLB Support Matrix
238c2ecf20Sopenharmony_ci *
248c2ecf20Sopenharmony_ci * ---------------------------------------------------
258c2ecf20Sopenharmony_ci * | Page Size | CONT PTE |  PMD  | CONT PMD |  PUD  |
268c2ecf20Sopenharmony_ci * ---------------------------------------------------
278c2ecf20Sopenharmony_ci * |     4K    |   64K    |   2M  |    32M   |   1G  |
288c2ecf20Sopenharmony_ci * |    16K    |    2M    |  32M  |     1G   |       |
298c2ecf20Sopenharmony_ci * |    64K    |    2M    | 512M  |    16G   |       |
308c2ecf20Sopenharmony_ci * ---------------------------------------------------
318c2ecf20Sopenharmony_ci */
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci/*
348c2ecf20Sopenharmony_ci * Reserve CMA areas for the largest supported gigantic
358c2ecf20Sopenharmony_ci * huge page when requested. Any other smaller gigantic
368c2ecf20Sopenharmony_ci * huge pages could still be served from those areas.
378c2ecf20Sopenharmony_ci */
388c2ecf20Sopenharmony_ci#ifdef CONFIG_CMA
398c2ecf20Sopenharmony_civoid __init arm64_hugetlb_cma_reserve(void)
408c2ecf20Sopenharmony_ci{
418c2ecf20Sopenharmony_ci	int order;
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci#ifdef CONFIG_ARM64_4K_PAGES
448c2ecf20Sopenharmony_ci	order = PUD_SHIFT - PAGE_SHIFT;
458c2ecf20Sopenharmony_ci#else
468c2ecf20Sopenharmony_ci	order = CONT_PMD_SHIFT - PAGE_SHIFT;
478c2ecf20Sopenharmony_ci#endif
488c2ecf20Sopenharmony_ci	/*
498c2ecf20Sopenharmony_ci	 * HugeTLB CMA reservation is required for gigantic
508c2ecf20Sopenharmony_ci	 * huge pages which could not be allocated via the
518c2ecf20Sopenharmony_ci	 * page allocator. Just warn if there is any change
528c2ecf20Sopenharmony_ci	 * breaking this assumption.
538c2ecf20Sopenharmony_ci	 */
548c2ecf20Sopenharmony_ci	WARN_ON(order <= MAX_ORDER);
558c2ecf20Sopenharmony_ci	hugetlb_cma_reserve(order);
568c2ecf20Sopenharmony_ci}
578c2ecf20Sopenharmony_ci#endif /* CONFIG_CMA */
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci#ifdef CONFIG_ARCH_ENABLE_HUGEPAGE_MIGRATION
608c2ecf20Sopenharmony_cibool arch_hugetlb_migration_supported(struct hstate *h)
618c2ecf20Sopenharmony_ci{
628c2ecf20Sopenharmony_ci	size_t pagesize = huge_page_size(h);
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	switch (pagesize) {
658c2ecf20Sopenharmony_ci#ifdef CONFIG_ARM64_4K_PAGES
668c2ecf20Sopenharmony_ci	case PUD_SIZE:
678c2ecf20Sopenharmony_ci#endif
688c2ecf20Sopenharmony_ci	case PMD_SIZE:
698c2ecf20Sopenharmony_ci	case CONT_PMD_SIZE:
708c2ecf20Sopenharmony_ci	case CONT_PTE_SIZE:
718c2ecf20Sopenharmony_ci		return true;
728c2ecf20Sopenharmony_ci	}
738c2ecf20Sopenharmony_ci	pr_warn("%s: unrecognized huge page size 0x%lx\n",
748c2ecf20Sopenharmony_ci			__func__, pagesize);
758c2ecf20Sopenharmony_ci	return false;
768c2ecf20Sopenharmony_ci}
778c2ecf20Sopenharmony_ci#endif
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ciint pmd_huge(pmd_t pmd)
808c2ecf20Sopenharmony_ci{
818c2ecf20Sopenharmony_ci	return pmd_val(pmd) && !(pmd_val(pmd) & PMD_TABLE_BIT);
828c2ecf20Sopenharmony_ci}
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ciint pud_huge(pud_t pud)
858c2ecf20Sopenharmony_ci{
868c2ecf20Sopenharmony_ci#ifndef __PAGETABLE_PMD_FOLDED
878c2ecf20Sopenharmony_ci	return pud_val(pud) && !(pud_val(pud) & PUD_TABLE_BIT);
888c2ecf20Sopenharmony_ci#else
898c2ecf20Sopenharmony_ci	return 0;
908c2ecf20Sopenharmony_ci#endif
918c2ecf20Sopenharmony_ci}
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci/*
948c2ecf20Sopenharmony_ci * Select all bits except the pfn
958c2ecf20Sopenharmony_ci */
968c2ecf20Sopenharmony_cistatic inline pgprot_t pte_pgprot(pte_t pte)
978c2ecf20Sopenharmony_ci{
988c2ecf20Sopenharmony_ci	unsigned long pfn = pte_pfn(pte);
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	return __pgprot(pte_val(pfn_pte(pfn, __pgprot(0))) ^ pte_val(pte));
1018c2ecf20Sopenharmony_ci}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_cistatic int find_num_contig(struct mm_struct *mm, unsigned long addr,
1048c2ecf20Sopenharmony_ci			   pte_t *ptep, size_t *pgsize)
1058c2ecf20Sopenharmony_ci{
1068c2ecf20Sopenharmony_ci	pgd_t *pgdp = pgd_offset(mm, addr);
1078c2ecf20Sopenharmony_ci	p4d_t *p4dp;
1088c2ecf20Sopenharmony_ci	pud_t *pudp;
1098c2ecf20Sopenharmony_ci	pmd_t *pmdp;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	*pgsize = PAGE_SIZE;
1128c2ecf20Sopenharmony_ci	p4dp = p4d_offset(pgdp, addr);
1138c2ecf20Sopenharmony_ci	pudp = pud_offset(p4dp, addr);
1148c2ecf20Sopenharmony_ci	pmdp = pmd_offset(pudp, addr);
1158c2ecf20Sopenharmony_ci	if ((pte_t *)pmdp == ptep) {
1168c2ecf20Sopenharmony_ci		*pgsize = PMD_SIZE;
1178c2ecf20Sopenharmony_ci		return CONT_PMDS;
1188c2ecf20Sopenharmony_ci	}
1198c2ecf20Sopenharmony_ci	return CONT_PTES;
1208c2ecf20Sopenharmony_ci}
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_cistatic inline int num_contig_ptes(unsigned long size, size_t *pgsize)
1238c2ecf20Sopenharmony_ci{
1248c2ecf20Sopenharmony_ci	int contig_ptes = 0;
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	*pgsize = size;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	switch (size) {
1298c2ecf20Sopenharmony_ci#ifdef CONFIG_ARM64_4K_PAGES
1308c2ecf20Sopenharmony_ci	case PUD_SIZE:
1318c2ecf20Sopenharmony_ci#endif
1328c2ecf20Sopenharmony_ci	case PMD_SIZE:
1338c2ecf20Sopenharmony_ci		contig_ptes = 1;
1348c2ecf20Sopenharmony_ci		break;
1358c2ecf20Sopenharmony_ci	case CONT_PMD_SIZE:
1368c2ecf20Sopenharmony_ci		*pgsize = PMD_SIZE;
1378c2ecf20Sopenharmony_ci		contig_ptes = CONT_PMDS;
1388c2ecf20Sopenharmony_ci		break;
1398c2ecf20Sopenharmony_ci	case CONT_PTE_SIZE:
1408c2ecf20Sopenharmony_ci		*pgsize = PAGE_SIZE;
1418c2ecf20Sopenharmony_ci		contig_ptes = CONT_PTES;
1428c2ecf20Sopenharmony_ci		break;
1438c2ecf20Sopenharmony_ci	}
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	return contig_ptes;
1468c2ecf20Sopenharmony_ci}
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci/*
1498c2ecf20Sopenharmony_ci * Changing some bits of contiguous entries requires us to follow a
1508c2ecf20Sopenharmony_ci * Break-Before-Make approach, breaking the whole contiguous set
1518c2ecf20Sopenharmony_ci * before we can change any entries. See ARM DDI 0487A.k_iss10775,
1528c2ecf20Sopenharmony_ci * "Misprogramming of the Contiguous bit", page D4-1762.
1538c2ecf20Sopenharmony_ci *
1548c2ecf20Sopenharmony_ci * This helper performs the break step.
1558c2ecf20Sopenharmony_ci */
1568c2ecf20Sopenharmony_cistatic pte_t get_clear_flush(struct mm_struct *mm,
1578c2ecf20Sopenharmony_ci			     unsigned long addr,
1588c2ecf20Sopenharmony_ci			     pte_t *ptep,
1598c2ecf20Sopenharmony_ci			     unsigned long pgsize,
1608c2ecf20Sopenharmony_ci			     unsigned long ncontig)
1618c2ecf20Sopenharmony_ci{
1628c2ecf20Sopenharmony_ci	pte_t orig_pte = huge_ptep_get(ptep);
1638c2ecf20Sopenharmony_ci	bool valid = pte_valid(orig_pte);
1648c2ecf20Sopenharmony_ci	unsigned long i, saddr = addr;
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	for (i = 0; i < ncontig; i++, addr += pgsize, ptep++) {
1678c2ecf20Sopenharmony_ci		pte_t pte = ptep_get_and_clear(mm, addr, ptep);
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci		/*
1708c2ecf20Sopenharmony_ci		 * If HW_AFDBM is enabled, then the HW could turn on
1718c2ecf20Sopenharmony_ci		 * the dirty or accessed bit for any page in the set,
1728c2ecf20Sopenharmony_ci		 * so check them all.
1738c2ecf20Sopenharmony_ci		 */
1748c2ecf20Sopenharmony_ci		if (pte_dirty(pte))
1758c2ecf20Sopenharmony_ci			orig_pte = pte_mkdirty(orig_pte);
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci		if (pte_young(pte))
1788c2ecf20Sopenharmony_ci			orig_pte = pte_mkyoung(orig_pte);
1798c2ecf20Sopenharmony_ci	}
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	if (valid) {
1828c2ecf20Sopenharmony_ci		struct vm_area_struct vma = TLB_FLUSH_VMA(mm, 0);
1838c2ecf20Sopenharmony_ci		flush_tlb_range(&vma, saddr, addr);
1848c2ecf20Sopenharmony_ci	}
1858c2ecf20Sopenharmony_ci	return orig_pte;
1868c2ecf20Sopenharmony_ci}
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci/*
1898c2ecf20Sopenharmony_ci * Changing some bits of contiguous entries requires us to follow a
1908c2ecf20Sopenharmony_ci * Break-Before-Make approach, breaking the whole contiguous set
1918c2ecf20Sopenharmony_ci * before we can change any entries. See ARM DDI 0487A.k_iss10775,
1928c2ecf20Sopenharmony_ci * "Misprogramming of the Contiguous bit", page D4-1762.
1938c2ecf20Sopenharmony_ci *
1948c2ecf20Sopenharmony_ci * This helper performs the break step for use cases where the
1958c2ecf20Sopenharmony_ci * original pte is not needed.
1968c2ecf20Sopenharmony_ci */
1978c2ecf20Sopenharmony_cistatic void clear_flush(struct mm_struct *mm,
1988c2ecf20Sopenharmony_ci			     unsigned long addr,
1998c2ecf20Sopenharmony_ci			     pte_t *ptep,
2008c2ecf20Sopenharmony_ci			     unsigned long pgsize,
2018c2ecf20Sopenharmony_ci			     unsigned long ncontig)
2028c2ecf20Sopenharmony_ci{
2038c2ecf20Sopenharmony_ci	struct vm_area_struct vma = TLB_FLUSH_VMA(mm, 0);
2048c2ecf20Sopenharmony_ci	unsigned long i, saddr = addr;
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	for (i = 0; i < ncontig; i++, addr += pgsize, ptep++)
2078c2ecf20Sopenharmony_ci		pte_clear(mm, addr, ptep);
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	flush_tlb_range(&vma, saddr, addr);
2108c2ecf20Sopenharmony_ci}
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_civoid set_huge_pte_at(struct mm_struct *mm, unsigned long addr,
2138c2ecf20Sopenharmony_ci			    pte_t *ptep, pte_t pte)
2148c2ecf20Sopenharmony_ci{
2158c2ecf20Sopenharmony_ci	size_t pgsize;
2168c2ecf20Sopenharmony_ci	int i;
2178c2ecf20Sopenharmony_ci	int ncontig;
2188c2ecf20Sopenharmony_ci	unsigned long pfn, dpfn;
2198c2ecf20Sopenharmony_ci	pgprot_t hugeprot;
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	/*
2228c2ecf20Sopenharmony_ci	 * Code needs to be expanded to handle huge swap and migration
2238c2ecf20Sopenharmony_ci	 * entries. Needed for HUGETLB and MEMORY_FAILURE.
2248c2ecf20Sopenharmony_ci	 */
2258c2ecf20Sopenharmony_ci	WARN_ON(!pte_present(pte));
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	if (!pte_cont(pte)) {
2288c2ecf20Sopenharmony_ci		set_pte_at(mm, addr, ptep, pte);
2298c2ecf20Sopenharmony_ci		return;
2308c2ecf20Sopenharmony_ci	}
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	ncontig = find_num_contig(mm, addr, ptep, &pgsize);
2338c2ecf20Sopenharmony_ci	pfn = pte_pfn(pte);
2348c2ecf20Sopenharmony_ci	dpfn = pgsize >> PAGE_SHIFT;
2358c2ecf20Sopenharmony_ci	hugeprot = pte_pgprot(pte);
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	clear_flush(mm, addr, ptep, pgsize, ncontig);
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	for (i = 0; i < ncontig; i++, ptep++, addr += pgsize, pfn += dpfn)
2408c2ecf20Sopenharmony_ci		set_pte_at(mm, addr, ptep, pfn_pte(pfn, hugeprot));
2418c2ecf20Sopenharmony_ci}
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_civoid set_huge_swap_pte_at(struct mm_struct *mm, unsigned long addr,
2448c2ecf20Sopenharmony_ci			  pte_t *ptep, pte_t pte, unsigned long sz)
2458c2ecf20Sopenharmony_ci{
2468c2ecf20Sopenharmony_ci	int i, ncontig;
2478c2ecf20Sopenharmony_ci	size_t pgsize;
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	ncontig = num_contig_ptes(sz, &pgsize);
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	for (i = 0; i < ncontig; i++, ptep++)
2528c2ecf20Sopenharmony_ci		set_pte(ptep, pte);
2538c2ecf20Sopenharmony_ci}
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_cipte_t *huge_pte_alloc(struct mm_struct *mm,
2568c2ecf20Sopenharmony_ci		      unsigned long addr, unsigned long sz)
2578c2ecf20Sopenharmony_ci{
2588c2ecf20Sopenharmony_ci	pgd_t *pgdp;
2598c2ecf20Sopenharmony_ci	p4d_t *p4dp;
2608c2ecf20Sopenharmony_ci	pud_t *pudp;
2618c2ecf20Sopenharmony_ci	pmd_t *pmdp;
2628c2ecf20Sopenharmony_ci	pte_t *ptep = NULL;
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	pgdp = pgd_offset(mm, addr);
2658c2ecf20Sopenharmony_ci	p4dp = p4d_offset(pgdp, addr);
2668c2ecf20Sopenharmony_ci	pudp = pud_alloc(mm, p4dp, addr);
2678c2ecf20Sopenharmony_ci	if (!pudp)
2688c2ecf20Sopenharmony_ci		return NULL;
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	if (sz == PUD_SIZE) {
2718c2ecf20Sopenharmony_ci		ptep = (pte_t *)pudp;
2728c2ecf20Sopenharmony_ci	} else if (sz == (CONT_PTE_SIZE)) {
2738c2ecf20Sopenharmony_ci		pmdp = pmd_alloc(mm, pudp, addr);
2748c2ecf20Sopenharmony_ci		if (!pmdp)
2758c2ecf20Sopenharmony_ci			return NULL;
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci		WARN_ON(addr & (sz - 1));
2788c2ecf20Sopenharmony_ci		/*
2798c2ecf20Sopenharmony_ci		 * Note that if this code were ever ported to the
2808c2ecf20Sopenharmony_ci		 * 32-bit arm platform then it will cause trouble in
2818c2ecf20Sopenharmony_ci		 * the case where CONFIG_HIGHPTE is set, since there
2828c2ecf20Sopenharmony_ci		 * will be no pte_unmap() to correspond with this
2838c2ecf20Sopenharmony_ci		 * pte_alloc_map().
2848c2ecf20Sopenharmony_ci		 */
2858c2ecf20Sopenharmony_ci		ptep = pte_alloc_map(mm, pmdp, addr);
2868c2ecf20Sopenharmony_ci	} else if (sz == PMD_SIZE) {
2878c2ecf20Sopenharmony_ci		if (IS_ENABLED(CONFIG_ARCH_WANT_HUGE_PMD_SHARE) &&
2888c2ecf20Sopenharmony_ci		    pud_none(READ_ONCE(*pudp)))
2898c2ecf20Sopenharmony_ci			ptep = huge_pmd_share(mm, addr, pudp);
2908c2ecf20Sopenharmony_ci		else
2918c2ecf20Sopenharmony_ci			ptep = (pte_t *)pmd_alloc(mm, pudp, addr);
2928c2ecf20Sopenharmony_ci	} else if (sz == (CONT_PMD_SIZE)) {
2938c2ecf20Sopenharmony_ci		pmdp = pmd_alloc(mm, pudp, addr);
2948c2ecf20Sopenharmony_ci		WARN_ON(addr & (sz - 1));
2958c2ecf20Sopenharmony_ci		return (pte_t *)pmdp;
2968c2ecf20Sopenharmony_ci	}
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	return ptep;
2998c2ecf20Sopenharmony_ci}
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_cipte_t *huge_pte_offset(struct mm_struct *mm,
3028c2ecf20Sopenharmony_ci		       unsigned long addr, unsigned long sz)
3038c2ecf20Sopenharmony_ci{
3048c2ecf20Sopenharmony_ci	pgd_t *pgdp;
3058c2ecf20Sopenharmony_ci	p4d_t *p4dp;
3068c2ecf20Sopenharmony_ci	pud_t *pudp, pud;
3078c2ecf20Sopenharmony_ci	pmd_t *pmdp, pmd;
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	pgdp = pgd_offset(mm, addr);
3108c2ecf20Sopenharmony_ci	if (!pgd_present(READ_ONCE(*pgdp)))
3118c2ecf20Sopenharmony_ci		return NULL;
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	p4dp = p4d_offset(pgdp, addr);
3148c2ecf20Sopenharmony_ci	if (!p4d_present(READ_ONCE(*p4dp)))
3158c2ecf20Sopenharmony_ci		return NULL;
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	pudp = pud_offset(p4dp, addr);
3188c2ecf20Sopenharmony_ci	pud = READ_ONCE(*pudp);
3198c2ecf20Sopenharmony_ci	if (sz != PUD_SIZE && pud_none(pud))
3208c2ecf20Sopenharmony_ci		return NULL;
3218c2ecf20Sopenharmony_ci	/* hugepage or swap? */
3228c2ecf20Sopenharmony_ci	if (pud_huge(pud) || !pud_present(pud))
3238c2ecf20Sopenharmony_ci		return (pte_t *)pudp;
3248c2ecf20Sopenharmony_ci	/* table; check the next level */
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	if (sz == CONT_PMD_SIZE)
3278c2ecf20Sopenharmony_ci		addr &= CONT_PMD_MASK;
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	pmdp = pmd_offset(pudp, addr);
3308c2ecf20Sopenharmony_ci	pmd = READ_ONCE(*pmdp);
3318c2ecf20Sopenharmony_ci	if (!(sz == PMD_SIZE || sz == CONT_PMD_SIZE) &&
3328c2ecf20Sopenharmony_ci	    pmd_none(pmd))
3338c2ecf20Sopenharmony_ci		return NULL;
3348c2ecf20Sopenharmony_ci	if (pmd_huge(pmd) || !pmd_present(pmd))
3358c2ecf20Sopenharmony_ci		return (pte_t *)pmdp;
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	if (sz == CONT_PTE_SIZE)
3388c2ecf20Sopenharmony_ci		return pte_offset_kernel(pmdp, (addr & CONT_PTE_MASK));
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	return NULL;
3418c2ecf20Sopenharmony_ci}
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_cipte_t arch_make_huge_pte(pte_t entry, struct vm_area_struct *vma,
3448c2ecf20Sopenharmony_ci			 struct page *page, int writable)
3458c2ecf20Sopenharmony_ci{
3468c2ecf20Sopenharmony_ci	size_t pagesize = huge_page_size(hstate_vma(vma));
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	if (pagesize == CONT_PTE_SIZE) {
3498c2ecf20Sopenharmony_ci		entry = pte_mkcont(entry);
3508c2ecf20Sopenharmony_ci	} else if (pagesize == CONT_PMD_SIZE) {
3518c2ecf20Sopenharmony_ci		entry = pmd_pte(pmd_mkcont(pte_pmd(entry)));
3528c2ecf20Sopenharmony_ci	} else if (pagesize != PUD_SIZE && pagesize != PMD_SIZE) {
3538c2ecf20Sopenharmony_ci		pr_warn("%s: unrecognized huge page size 0x%lx\n",
3548c2ecf20Sopenharmony_ci			__func__, pagesize);
3558c2ecf20Sopenharmony_ci	}
3568c2ecf20Sopenharmony_ci	return entry;
3578c2ecf20Sopenharmony_ci}
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_civoid huge_pte_clear(struct mm_struct *mm, unsigned long addr,
3608c2ecf20Sopenharmony_ci		    pte_t *ptep, unsigned long sz)
3618c2ecf20Sopenharmony_ci{
3628c2ecf20Sopenharmony_ci	int i, ncontig;
3638c2ecf20Sopenharmony_ci	size_t pgsize;
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	ncontig = num_contig_ptes(sz, &pgsize);
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	for (i = 0; i < ncontig; i++, addr += pgsize, ptep++)
3688c2ecf20Sopenharmony_ci		pte_clear(mm, addr, ptep);
3698c2ecf20Sopenharmony_ci}
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_cipte_t huge_ptep_get_and_clear(struct mm_struct *mm,
3728c2ecf20Sopenharmony_ci			      unsigned long addr, pte_t *ptep)
3738c2ecf20Sopenharmony_ci{
3748c2ecf20Sopenharmony_ci	int ncontig;
3758c2ecf20Sopenharmony_ci	size_t pgsize;
3768c2ecf20Sopenharmony_ci	pte_t orig_pte = huge_ptep_get(ptep);
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	if (!pte_cont(orig_pte))
3798c2ecf20Sopenharmony_ci		return ptep_get_and_clear(mm, addr, ptep);
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	ncontig = find_num_contig(mm, addr, ptep, &pgsize);
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	return get_clear_flush(mm, addr, ptep, pgsize, ncontig);
3848c2ecf20Sopenharmony_ci}
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci/*
3878c2ecf20Sopenharmony_ci * huge_ptep_set_access_flags will update access flags (dirty, accesssed)
3888c2ecf20Sopenharmony_ci * and write permission.
3898c2ecf20Sopenharmony_ci *
3908c2ecf20Sopenharmony_ci * For a contiguous huge pte range we need to check whether or not write
3918c2ecf20Sopenharmony_ci * permission has to change only on the first pte in the set. Then for
3928c2ecf20Sopenharmony_ci * all the contiguous ptes we need to check whether or not there is a
3938c2ecf20Sopenharmony_ci * discrepancy between dirty or young.
3948c2ecf20Sopenharmony_ci */
3958c2ecf20Sopenharmony_cistatic int __cont_access_flags_changed(pte_t *ptep, pte_t pte, int ncontig)
3968c2ecf20Sopenharmony_ci{
3978c2ecf20Sopenharmony_ci	int i;
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	if (pte_write(pte) != pte_write(huge_ptep_get(ptep)))
4008c2ecf20Sopenharmony_ci		return 1;
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	for (i = 0; i < ncontig; i++) {
4038c2ecf20Sopenharmony_ci		pte_t orig_pte = huge_ptep_get(ptep + i);
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci		if (pte_dirty(pte) != pte_dirty(orig_pte))
4068c2ecf20Sopenharmony_ci			return 1;
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci		if (pte_young(pte) != pte_young(orig_pte))
4098c2ecf20Sopenharmony_ci			return 1;
4108c2ecf20Sopenharmony_ci	}
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci	return 0;
4138c2ecf20Sopenharmony_ci}
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ciint huge_ptep_set_access_flags(struct vm_area_struct *vma,
4168c2ecf20Sopenharmony_ci			       unsigned long addr, pte_t *ptep,
4178c2ecf20Sopenharmony_ci			       pte_t pte, int dirty)
4188c2ecf20Sopenharmony_ci{
4198c2ecf20Sopenharmony_ci	int ncontig, i;
4208c2ecf20Sopenharmony_ci	size_t pgsize = 0;
4218c2ecf20Sopenharmony_ci	unsigned long pfn = pte_pfn(pte), dpfn;
4228c2ecf20Sopenharmony_ci	pgprot_t hugeprot;
4238c2ecf20Sopenharmony_ci	pte_t orig_pte;
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	if (!pte_cont(pte))
4268c2ecf20Sopenharmony_ci		return ptep_set_access_flags(vma, addr, ptep, pte, dirty);
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	ncontig = find_num_contig(vma->vm_mm, addr, ptep, &pgsize);
4298c2ecf20Sopenharmony_ci	dpfn = pgsize >> PAGE_SHIFT;
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	if (!__cont_access_flags_changed(ptep, pte, ncontig))
4328c2ecf20Sopenharmony_ci		return 0;
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	orig_pte = get_clear_flush(vma->vm_mm, addr, ptep, pgsize, ncontig);
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci	/* Make sure we don't lose the dirty or young state */
4378c2ecf20Sopenharmony_ci	if (pte_dirty(orig_pte))
4388c2ecf20Sopenharmony_ci		pte = pte_mkdirty(pte);
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci	if (pte_young(orig_pte))
4418c2ecf20Sopenharmony_ci		pte = pte_mkyoung(pte);
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci	hugeprot = pte_pgprot(pte);
4448c2ecf20Sopenharmony_ci	for (i = 0; i < ncontig; i++, ptep++, addr += pgsize, pfn += dpfn)
4458c2ecf20Sopenharmony_ci		set_pte_at(vma->vm_mm, addr, ptep, pfn_pte(pfn, hugeprot));
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci	return 1;
4488c2ecf20Sopenharmony_ci}
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_civoid huge_ptep_set_wrprotect(struct mm_struct *mm,
4518c2ecf20Sopenharmony_ci			     unsigned long addr, pte_t *ptep)
4528c2ecf20Sopenharmony_ci{
4538c2ecf20Sopenharmony_ci	unsigned long pfn, dpfn;
4548c2ecf20Sopenharmony_ci	pgprot_t hugeprot;
4558c2ecf20Sopenharmony_ci	int ncontig, i;
4568c2ecf20Sopenharmony_ci	size_t pgsize;
4578c2ecf20Sopenharmony_ci	pte_t pte;
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	if (!pte_cont(READ_ONCE(*ptep))) {
4608c2ecf20Sopenharmony_ci		ptep_set_wrprotect(mm, addr, ptep);
4618c2ecf20Sopenharmony_ci		return;
4628c2ecf20Sopenharmony_ci	}
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci	ncontig = find_num_contig(mm, addr, ptep, &pgsize);
4658c2ecf20Sopenharmony_ci	dpfn = pgsize >> PAGE_SHIFT;
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci	pte = get_clear_flush(mm, addr, ptep, pgsize, ncontig);
4688c2ecf20Sopenharmony_ci	pte = pte_wrprotect(pte);
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	hugeprot = pte_pgprot(pte);
4718c2ecf20Sopenharmony_ci	pfn = pte_pfn(pte);
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci	for (i = 0; i < ncontig; i++, ptep++, addr += pgsize, pfn += dpfn)
4748c2ecf20Sopenharmony_ci		set_pte_at(mm, addr, ptep, pfn_pte(pfn, hugeprot));
4758c2ecf20Sopenharmony_ci}
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_civoid huge_ptep_clear_flush(struct vm_area_struct *vma,
4788c2ecf20Sopenharmony_ci			   unsigned long addr, pte_t *ptep)
4798c2ecf20Sopenharmony_ci{
4808c2ecf20Sopenharmony_ci	size_t pgsize;
4818c2ecf20Sopenharmony_ci	int ncontig;
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	if (!pte_cont(READ_ONCE(*ptep))) {
4848c2ecf20Sopenharmony_ci		ptep_clear_flush(vma, addr, ptep);
4858c2ecf20Sopenharmony_ci		return;
4868c2ecf20Sopenharmony_ci	}
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci	ncontig = find_num_contig(vma->vm_mm, addr, ptep, &pgsize);
4898c2ecf20Sopenharmony_ci	clear_flush(vma->vm_mm, addr, ptep, pgsize, ncontig);
4908c2ecf20Sopenharmony_ci}
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_cistatic int __init hugetlbpage_init(void)
4938c2ecf20Sopenharmony_ci{
4948c2ecf20Sopenharmony_ci#ifdef CONFIG_ARM64_4K_PAGES
4958c2ecf20Sopenharmony_ci	hugetlb_add_hstate(PUD_SHIFT - PAGE_SHIFT);
4968c2ecf20Sopenharmony_ci#endif
4978c2ecf20Sopenharmony_ci	hugetlb_add_hstate(CONT_PMD_SHIFT - PAGE_SHIFT);
4988c2ecf20Sopenharmony_ci	hugetlb_add_hstate(PMD_SHIFT - PAGE_SHIFT);
4998c2ecf20Sopenharmony_ci	hugetlb_add_hstate(CONT_PTE_SHIFT - PAGE_SHIFT);
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci	return 0;
5028c2ecf20Sopenharmony_ci}
5038c2ecf20Sopenharmony_ciarch_initcall(hugetlbpage_init);
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_cibool __init arch_hugetlb_valid_size(unsigned long size)
5068c2ecf20Sopenharmony_ci{
5078c2ecf20Sopenharmony_ci	switch (size) {
5088c2ecf20Sopenharmony_ci#ifdef CONFIG_ARM64_4K_PAGES
5098c2ecf20Sopenharmony_ci	case PUD_SIZE:
5108c2ecf20Sopenharmony_ci#endif
5118c2ecf20Sopenharmony_ci	case CONT_PMD_SIZE:
5128c2ecf20Sopenharmony_ci	case PMD_SIZE:
5138c2ecf20Sopenharmony_ci	case CONT_PTE_SIZE:
5148c2ecf20Sopenharmony_ci		return true;
5158c2ecf20Sopenharmony_ci	}
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	return false;
5188c2ecf20Sopenharmony_ci}
519