162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * SPARC64 Huge TLB page support.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2002, 2003, 2006 David S. Miller (davem@davemloft.net)
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/fs.h>
962306a36Sopenharmony_ci#include <linux/mm.h>
1062306a36Sopenharmony_ci#include <linux/sched/mm.h>
1162306a36Sopenharmony_ci#include <linux/hugetlb.h>
1262306a36Sopenharmony_ci#include <linux/pagemap.h>
1362306a36Sopenharmony_ci#include <linux/sysctl.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include <asm/mman.h>
1662306a36Sopenharmony_ci#include <asm/pgalloc.h>
1762306a36Sopenharmony_ci#include <asm/tlb.h>
1862306a36Sopenharmony_ci#include <asm/tlbflush.h>
1962306a36Sopenharmony_ci#include <asm/cacheflush.h>
2062306a36Sopenharmony_ci#include <asm/mmu_context.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci/* Slightly simplified from the non-hugepage variant because by
2362306a36Sopenharmony_ci * definition we don't have to worry about any page coloring stuff
2462306a36Sopenharmony_ci */
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_cistatic unsigned long hugetlb_get_unmapped_area_bottomup(struct file *filp,
2762306a36Sopenharmony_ci							unsigned long addr,
2862306a36Sopenharmony_ci							unsigned long len,
2962306a36Sopenharmony_ci							unsigned long pgoff,
3062306a36Sopenharmony_ci							unsigned long flags)
3162306a36Sopenharmony_ci{
3262306a36Sopenharmony_ci	struct hstate *h = hstate_file(filp);
3362306a36Sopenharmony_ci	unsigned long task_size = TASK_SIZE;
3462306a36Sopenharmony_ci	struct vm_unmapped_area_info info;
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	if (test_thread_flag(TIF_32BIT))
3762306a36Sopenharmony_ci		task_size = STACK_TOP32;
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	info.flags = 0;
4062306a36Sopenharmony_ci	info.length = len;
4162306a36Sopenharmony_ci	info.low_limit = TASK_UNMAPPED_BASE;
4262306a36Sopenharmony_ci	info.high_limit = min(task_size, VA_EXCLUDE_START);
4362306a36Sopenharmony_ci	info.align_mask = PAGE_MASK & ~huge_page_mask(h);
4462306a36Sopenharmony_ci	info.align_offset = 0;
4562306a36Sopenharmony_ci	addr = vm_unmapped_area(&info);
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	if ((addr & ~PAGE_MASK) && task_size > VA_EXCLUDE_END) {
4862306a36Sopenharmony_ci		VM_BUG_ON(addr != -ENOMEM);
4962306a36Sopenharmony_ci		info.low_limit = VA_EXCLUDE_END;
5062306a36Sopenharmony_ci		info.high_limit = task_size;
5162306a36Sopenharmony_ci		addr = vm_unmapped_area(&info);
5262306a36Sopenharmony_ci	}
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	return addr;
5562306a36Sopenharmony_ci}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistatic unsigned long
5862306a36Sopenharmony_cihugetlb_get_unmapped_area_topdown(struct file *filp, const unsigned long addr0,
5962306a36Sopenharmony_ci				  const unsigned long len,
6062306a36Sopenharmony_ci				  const unsigned long pgoff,
6162306a36Sopenharmony_ci				  const unsigned long flags)
6262306a36Sopenharmony_ci{
6362306a36Sopenharmony_ci	struct hstate *h = hstate_file(filp);
6462306a36Sopenharmony_ci	struct mm_struct *mm = current->mm;
6562306a36Sopenharmony_ci	unsigned long addr = addr0;
6662306a36Sopenharmony_ci	struct vm_unmapped_area_info info;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	/* This should only ever run for 32-bit processes.  */
6962306a36Sopenharmony_ci	BUG_ON(!test_thread_flag(TIF_32BIT));
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	info.flags = VM_UNMAPPED_AREA_TOPDOWN;
7262306a36Sopenharmony_ci	info.length = len;
7362306a36Sopenharmony_ci	info.low_limit = PAGE_SIZE;
7462306a36Sopenharmony_ci	info.high_limit = mm->mmap_base;
7562306a36Sopenharmony_ci	info.align_mask = PAGE_MASK & ~huge_page_mask(h);
7662306a36Sopenharmony_ci	info.align_offset = 0;
7762306a36Sopenharmony_ci	addr = vm_unmapped_area(&info);
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	/*
8062306a36Sopenharmony_ci	 * A failed mmap() very likely causes application failure,
8162306a36Sopenharmony_ci	 * so fall back to the bottom-up function here. This scenario
8262306a36Sopenharmony_ci	 * can happen with large stack limits and large mmap()
8362306a36Sopenharmony_ci	 * allocations.
8462306a36Sopenharmony_ci	 */
8562306a36Sopenharmony_ci	if (addr & ~PAGE_MASK) {
8662306a36Sopenharmony_ci		VM_BUG_ON(addr != -ENOMEM);
8762306a36Sopenharmony_ci		info.flags = 0;
8862306a36Sopenharmony_ci		info.low_limit = TASK_UNMAPPED_BASE;
8962306a36Sopenharmony_ci		info.high_limit = STACK_TOP32;
9062306a36Sopenharmony_ci		addr = vm_unmapped_area(&info);
9162306a36Sopenharmony_ci	}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	return addr;
9462306a36Sopenharmony_ci}
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ciunsigned long
9762306a36Sopenharmony_cihugetlb_get_unmapped_area(struct file *file, unsigned long addr,
9862306a36Sopenharmony_ci		unsigned long len, unsigned long pgoff, unsigned long flags)
9962306a36Sopenharmony_ci{
10062306a36Sopenharmony_ci	struct hstate *h = hstate_file(file);
10162306a36Sopenharmony_ci	struct mm_struct *mm = current->mm;
10262306a36Sopenharmony_ci	struct vm_area_struct *vma;
10362306a36Sopenharmony_ci	unsigned long task_size = TASK_SIZE;
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	if (test_thread_flag(TIF_32BIT))
10662306a36Sopenharmony_ci		task_size = STACK_TOP32;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	if (len & ~huge_page_mask(h))
10962306a36Sopenharmony_ci		return -EINVAL;
11062306a36Sopenharmony_ci	if (len > task_size)
11162306a36Sopenharmony_ci		return -ENOMEM;
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	if (flags & MAP_FIXED) {
11462306a36Sopenharmony_ci		if (prepare_hugepage_range(file, addr, len))
11562306a36Sopenharmony_ci			return -EINVAL;
11662306a36Sopenharmony_ci		return addr;
11762306a36Sopenharmony_ci	}
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	if (addr) {
12062306a36Sopenharmony_ci		addr = ALIGN(addr, huge_page_size(h));
12162306a36Sopenharmony_ci		vma = find_vma(mm, addr);
12262306a36Sopenharmony_ci		if (task_size - len >= addr &&
12362306a36Sopenharmony_ci		    (!vma || addr + len <= vm_start_gap(vma)))
12462306a36Sopenharmony_ci			return addr;
12562306a36Sopenharmony_ci	}
12662306a36Sopenharmony_ci	if (mm->get_unmapped_area == arch_get_unmapped_area)
12762306a36Sopenharmony_ci		return hugetlb_get_unmapped_area_bottomup(file, addr, len,
12862306a36Sopenharmony_ci				pgoff, flags);
12962306a36Sopenharmony_ci	else
13062306a36Sopenharmony_ci		return hugetlb_get_unmapped_area_topdown(file, addr, len,
13162306a36Sopenharmony_ci				pgoff, flags);
13262306a36Sopenharmony_ci}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_cistatic pte_t sun4u_hugepage_shift_to_tte(pte_t entry, unsigned int shift)
13562306a36Sopenharmony_ci{
13662306a36Sopenharmony_ci	return entry;
13762306a36Sopenharmony_ci}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_cistatic pte_t sun4v_hugepage_shift_to_tte(pte_t entry, unsigned int shift)
14062306a36Sopenharmony_ci{
14162306a36Sopenharmony_ci	unsigned long hugepage_size = _PAGE_SZ4MB_4V;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	pte_val(entry) = pte_val(entry) & ~_PAGE_SZALL_4V;
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	switch (shift) {
14662306a36Sopenharmony_ci	case HPAGE_16GB_SHIFT:
14762306a36Sopenharmony_ci		hugepage_size = _PAGE_SZ16GB_4V;
14862306a36Sopenharmony_ci		pte_val(entry) |= _PAGE_PUD_HUGE;
14962306a36Sopenharmony_ci		break;
15062306a36Sopenharmony_ci	case HPAGE_2GB_SHIFT:
15162306a36Sopenharmony_ci		hugepage_size = _PAGE_SZ2GB_4V;
15262306a36Sopenharmony_ci		pte_val(entry) |= _PAGE_PMD_HUGE;
15362306a36Sopenharmony_ci		break;
15462306a36Sopenharmony_ci	case HPAGE_256MB_SHIFT:
15562306a36Sopenharmony_ci		hugepage_size = _PAGE_SZ256MB_4V;
15662306a36Sopenharmony_ci		pte_val(entry) |= _PAGE_PMD_HUGE;
15762306a36Sopenharmony_ci		break;
15862306a36Sopenharmony_ci	case HPAGE_SHIFT:
15962306a36Sopenharmony_ci		pte_val(entry) |= _PAGE_PMD_HUGE;
16062306a36Sopenharmony_ci		break;
16162306a36Sopenharmony_ci	case HPAGE_64K_SHIFT:
16262306a36Sopenharmony_ci		hugepage_size = _PAGE_SZ64K_4V;
16362306a36Sopenharmony_ci		break;
16462306a36Sopenharmony_ci	default:
16562306a36Sopenharmony_ci		WARN_ONCE(1, "unsupported hugepage shift=%u\n", shift);
16662306a36Sopenharmony_ci	}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	pte_val(entry) = pte_val(entry) | hugepage_size;
16962306a36Sopenharmony_ci	return entry;
17062306a36Sopenharmony_ci}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_cistatic pte_t hugepage_shift_to_tte(pte_t entry, unsigned int shift)
17362306a36Sopenharmony_ci{
17462306a36Sopenharmony_ci	if (tlb_type == hypervisor)
17562306a36Sopenharmony_ci		return sun4v_hugepage_shift_to_tte(entry, shift);
17662306a36Sopenharmony_ci	else
17762306a36Sopenharmony_ci		return sun4u_hugepage_shift_to_tte(entry, shift);
17862306a36Sopenharmony_ci}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_cipte_t arch_make_huge_pte(pte_t entry, unsigned int shift, vm_flags_t flags)
18162306a36Sopenharmony_ci{
18262306a36Sopenharmony_ci	pte_t pte;
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	entry = pte_mkhuge(entry);
18562306a36Sopenharmony_ci	pte = hugepage_shift_to_tte(entry, shift);
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci#ifdef CONFIG_SPARC64
18862306a36Sopenharmony_ci	/* If this vma has ADI enabled on it, turn on TTE.mcd
18962306a36Sopenharmony_ci	 */
19062306a36Sopenharmony_ci	if (flags & VM_SPARC_ADI)
19162306a36Sopenharmony_ci		return pte_mkmcd(pte);
19262306a36Sopenharmony_ci	else
19362306a36Sopenharmony_ci		return pte_mknotmcd(pte);
19462306a36Sopenharmony_ci#else
19562306a36Sopenharmony_ci	return pte;
19662306a36Sopenharmony_ci#endif
19762306a36Sopenharmony_ci}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_cistatic unsigned int sun4v_huge_tte_to_shift(pte_t entry)
20062306a36Sopenharmony_ci{
20162306a36Sopenharmony_ci	unsigned long tte_szbits = pte_val(entry) & _PAGE_SZALL_4V;
20262306a36Sopenharmony_ci	unsigned int shift;
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	switch (tte_szbits) {
20562306a36Sopenharmony_ci	case _PAGE_SZ16GB_4V:
20662306a36Sopenharmony_ci		shift = HPAGE_16GB_SHIFT;
20762306a36Sopenharmony_ci		break;
20862306a36Sopenharmony_ci	case _PAGE_SZ2GB_4V:
20962306a36Sopenharmony_ci		shift = HPAGE_2GB_SHIFT;
21062306a36Sopenharmony_ci		break;
21162306a36Sopenharmony_ci	case _PAGE_SZ256MB_4V:
21262306a36Sopenharmony_ci		shift = HPAGE_256MB_SHIFT;
21362306a36Sopenharmony_ci		break;
21462306a36Sopenharmony_ci	case _PAGE_SZ4MB_4V:
21562306a36Sopenharmony_ci		shift = REAL_HPAGE_SHIFT;
21662306a36Sopenharmony_ci		break;
21762306a36Sopenharmony_ci	case _PAGE_SZ64K_4V:
21862306a36Sopenharmony_ci		shift = HPAGE_64K_SHIFT;
21962306a36Sopenharmony_ci		break;
22062306a36Sopenharmony_ci	default:
22162306a36Sopenharmony_ci		shift = PAGE_SHIFT;
22262306a36Sopenharmony_ci		break;
22362306a36Sopenharmony_ci	}
22462306a36Sopenharmony_ci	return shift;
22562306a36Sopenharmony_ci}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_cistatic unsigned int sun4u_huge_tte_to_shift(pte_t entry)
22862306a36Sopenharmony_ci{
22962306a36Sopenharmony_ci	unsigned long tte_szbits = pte_val(entry) & _PAGE_SZALL_4U;
23062306a36Sopenharmony_ci	unsigned int shift;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	switch (tte_szbits) {
23362306a36Sopenharmony_ci	case _PAGE_SZ256MB_4U:
23462306a36Sopenharmony_ci		shift = HPAGE_256MB_SHIFT;
23562306a36Sopenharmony_ci		break;
23662306a36Sopenharmony_ci	case _PAGE_SZ4MB_4U:
23762306a36Sopenharmony_ci		shift = REAL_HPAGE_SHIFT;
23862306a36Sopenharmony_ci		break;
23962306a36Sopenharmony_ci	case _PAGE_SZ64K_4U:
24062306a36Sopenharmony_ci		shift = HPAGE_64K_SHIFT;
24162306a36Sopenharmony_ci		break;
24262306a36Sopenharmony_ci	default:
24362306a36Sopenharmony_ci		shift = PAGE_SHIFT;
24462306a36Sopenharmony_ci		break;
24562306a36Sopenharmony_ci	}
24662306a36Sopenharmony_ci	return shift;
24762306a36Sopenharmony_ci}
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_cistatic unsigned long tte_to_shift(pte_t entry)
25062306a36Sopenharmony_ci{
25162306a36Sopenharmony_ci	if (tlb_type == hypervisor)
25262306a36Sopenharmony_ci		return sun4v_huge_tte_to_shift(entry);
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	return sun4u_huge_tte_to_shift(entry);
25562306a36Sopenharmony_ci}
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_cistatic unsigned int huge_tte_to_shift(pte_t entry)
25862306a36Sopenharmony_ci{
25962306a36Sopenharmony_ci	unsigned long shift = tte_to_shift(entry);
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	if (shift == PAGE_SHIFT)
26262306a36Sopenharmony_ci		WARN_ONCE(1, "tto_to_shift: invalid hugepage tte=0x%lx\n",
26362306a36Sopenharmony_ci			  pte_val(entry));
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	return shift;
26662306a36Sopenharmony_ci}
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_cistatic unsigned long huge_tte_to_size(pte_t pte)
26962306a36Sopenharmony_ci{
27062306a36Sopenharmony_ci	unsigned long size = 1UL << huge_tte_to_shift(pte);
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	if (size == REAL_HPAGE_SIZE)
27362306a36Sopenharmony_ci		size = HPAGE_SIZE;
27462306a36Sopenharmony_ci	return size;
27562306a36Sopenharmony_ci}
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ciunsigned long pud_leaf_size(pud_t pud) { return 1UL << tte_to_shift(*(pte_t *)&pud); }
27862306a36Sopenharmony_ciunsigned long pmd_leaf_size(pmd_t pmd) { return 1UL << tte_to_shift(*(pte_t *)&pmd); }
27962306a36Sopenharmony_ciunsigned long pte_leaf_size(pte_t pte) { return 1UL << tte_to_shift(pte); }
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_cipte_t *huge_pte_alloc(struct mm_struct *mm, struct vm_area_struct *vma,
28262306a36Sopenharmony_ci			unsigned long addr, unsigned long sz)
28362306a36Sopenharmony_ci{
28462306a36Sopenharmony_ci	pgd_t *pgd;
28562306a36Sopenharmony_ci	p4d_t *p4d;
28662306a36Sopenharmony_ci	pud_t *pud;
28762306a36Sopenharmony_ci	pmd_t *pmd;
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	pgd = pgd_offset(mm, addr);
29062306a36Sopenharmony_ci	p4d = p4d_offset(pgd, addr);
29162306a36Sopenharmony_ci	pud = pud_alloc(mm, p4d, addr);
29262306a36Sopenharmony_ci	if (!pud)
29362306a36Sopenharmony_ci		return NULL;
29462306a36Sopenharmony_ci	if (sz >= PUD_SIZE)
29562306a36Sopenharmony_ci		return (pte_t *)pud;
29662306a36Sopenharmony_ci	pmd = pmd_alloc(mm, pud, addr);
29762306a36Sopenharmony_ci	if (!pmd)
29862306a36Sopenharmony_ci		return NULL;
29962306a36Sopenharmony_ci	if (sz >= PMD_SIZE)
30062306a36Sopenharmony_ci		return (pte_t *)pmd;
30162306a36Sopenharmony_ci	return pte_alloc_huge(mm, pmd, addr);
30262306a36Sopenharmony_ci}
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_cipte_t *huge_pte_offset(struct mm_struct *mm,
30562306a36Sopenharmony_ci		       unsigned long addr, unsigned long sz)
30662306a36Sopenharmony_ci{
30762306a36Sopenharmony_ci	pgd_t *pgd;
30862306a36Sopenharmony_ci	p4d_t *p4d;
30962306a36Sopenharmony_ci	pud_t *pud;
31062306a36Sopenharmony_ci	pmd_t *pmd;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	pgd = pgd_offset(mm, addr);
31362306a36Sopenharmony_ci	if (pgd_none(*pgd))
31462306a36Sopenharmony_ci		return NULL;
31562306a36Sopenharmony_ci	p4d = p4d_offset(pgd, addr);
31662306a36Sopenharmony_ci	if (p4d_none(*p4d))
31762306a36Sopenharmony_ci		return NULL;
31862306a36Sopenharmony_ci	pud = pud_offset(p4d, addr);
31962306a36Sopenharmony_ci	if (pud_none(*pud))
32062306a36Sopenharmony_ci		return NULL;
32162306a36Sopenharmony_ci	if (is_hugetlb_pud(*pud))
32262306a36Sopenharmony_ci		return (pte_t *)pud;
32362306a36Sopenharmony_ci	pmd = pmd_offset(pud, addr);
32462306a36Sopenharmony_ci	if (pmd_none(*pmd))
32562306a36Sopenharmony_ci		return NULL;
32662306a36Sopenharmony_ci	if (is_hugetlb_pmd(*pmd))
32762306a36Sopenharmony_ci		return (pte_t *)pmd;
32862306a36Sopenharmony_ci	return pte_offset_huge(pmd, addr);
32962306a36Sopenharmony_ci}
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_civoid __set_huge_pte_at(struct mm_struct *mm, unsigned long addr,
33262306a36Sopenharmony_ci		     pte_t *ptep, pte_t entry)
33362306a36Sopenharmony_ci{
33462306a36Sopenharmony_ci	unsigned int nptes, orig_shift, shift;
33562306a36Sopenharmony_ci	unsigned long i, size;
33662306a36Sopenharmony_ci	pte_t orig;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	size = huge_tte_to_size(entry);
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	shift = PAGE_SHIFT;
34162306a36Sopenharmony_ci	if (size >= PUD_SIZE)
34262306a36Sopenharmony_ci		shift = PUD_SHIFT;
34362306a36Sopenharmony_ci	else if (size >= PMD_SIZE)
34462306a36Sopenharmony_ci		shift = PMD_SHIFT;
34562306a36Sopenharmony_ci	else
34662306a36Sopenharmony_ci		shift = PAGE_SHIFT;
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	nptes = size >> shift;
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	if (!pte_present(*ptep) && pte_present(entry))
35162306a36Sopenharmony_ci		mm->context.hugetlb_pte_count += nptes;
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	addr &= ~(size - 1);
35462306a36Sopenharmony_ci	orig = *ptep;
35562306a36Sopenharmony_ci	orig_shift = pte_none(orig) ? PAGE_SHIFT : huge_tte_to_shift(orig);
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	for (i = 0; i < nptes; i++)
35862306a36Sopenharmony_ci		ptep[i] = __pte(pte_val(entry) + (i << shift));
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	maybe_tlb_batch_add(mm, addr, ptep, orig, 0, orig_shift);
36162306a36Sopenharmony_ci	/* An HPAGE_SIZE'ed page is composed of two REAL_HPAGE_SIZE'ed pages */
36262306a36Sopenharmony_ci	if (size == HPAGE_SIZE)
36362306a36Sopenharmony_ci		maybe_tlb_batch_add(mm, addr + REAL_HPAGE_SIZE, ptep, orig, 0,
36462306a36Sopenharmony_ci				    orig_shift);
36562306a36Sopenharmony_ci}
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_civoid set_huge_pte_at(struct mm_struct *mm, unsigned long addr,
36862306a36Sopenharmony_ci		     pte_t *ptep, pte_t entry, unsigned long sz)
36962306a36Sopenharmony_ci{
37062306a36Sopenharmony_ci	__set_huge_pte_at(mm, addr, ptep, entry);
37162306a36Sopenharmony_ci}
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_cipte_t huge_ptep_get_and_clear(struct mm_struct *mm, unsigned long addr,
37462306a36Sopenharmony_ci			      pte_t *ptep)
37562306a36Sopenharmony_ci{
37662306a36Sopenharmony_ci	unsigned int i, nptes, orig_shift, shift;
37762306a36Sopenharmony_ci	unsigned long size;
37862306a36Sopenharmony_ci	pte_t entry;
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	entry = *ptep;
38162306a36Sopenharmony_ci	size = huge_tte_to_size(entry);
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	shift = PAGE_SHIFT;
38462306a36Sopenharmony_ci	if (size >= PUD_SIZE)
38562306a36Sopenharmony_ci		shift = PUD_SHIFT;
38662306a36Sopenharmony_ci	else if (size >= PMD_SIZE)
38762306a36Sopenharmony_ci		shift = PMD_SHIFT;
38862306a36Sopenharmony_ci	else
38962306a36Sopenharmony_ci		shift = PAGE_SHIFT;
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	nptes = size >> shift;
39262306a36Sopenharmony_ci	orig_shift = pte_none(entry) ? PAGE_SHIFT : huge_tte_to_shift(entry);
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	if (pte_present(entry))
39562306a36Sopenharmony_ci		mm->context.hugetlb_pte_count -= nptes;
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	addr &= ~(size - 1);
39862306a36Sopenharmony_ci	for (i = 0; i < nptes; i++)
39962306a36Sopenharmony_ci		ptep[i] = __pte(0UL);
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	maybe_tlb_batch_add(mm, addr, ptep, entry, 0, orig_shift);
40262306a36Sopenharmony_ci	/* An HPAGE_SIZE'ed page is composed of two REAL_HPAGE_SIZE'ed pages */
40362306a36Sopenharmony_ci	if (size == HPAGE_SIZE)
40462306a36Sopenharmony_ci		maybe_tlb_batch_add(mm, addr + REAL_HPAGE_SIZE, ptep, entry, 0,
40562306a36Sopenharmony_ci				    orig_shift);
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	return entry;
40862306a36Sopenharmony_ci}
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ciint pmd_huge(pmd_t pmd)
41162306a36Sopenharmony_ci{
41262306a36Sopenharmony_ci	return !pmd_none(pmd) &&
41362306a36Sopenharmony_ci		(pmd_val(pmd) & (_PAGE_VALID|_PAGE_PMD_HUGE)) != _PAGE_VALID;
41462306a36Sopenharmony_ci}
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ciint pud_huge(pud_t pud)
41762306a36Sopenharmony_ci{
41862306a36Sopenharmony_ci	return !pud_none(pud) &&
41962306a36Sopenharmony_ci		(pud_val(pud) & (_PAGE_VALID|_PAGE_PUD_HUGE)) != _PAGE_VALID;
42062306a36Sopenharmony_ci}
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_cistatic void hugetlb_free_pte_range(struct mmu_gather *tlb, pmd_t *pmd,
42362306a36Sopenharmony_ci			   unsigned long addr)
42462306a36Sopenharmony_ci{
42562306a36Sopenharmony_ci	pgtable_t token = pmd_pgtable(*pmd);
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	pmd_clear(pmd);
42862306a36Sopenharmony_ci	pte_free_tlb(tlb, token, addr);
42962306a36Sopenharmony_ci	mm_dec_nr_ptes(tlb->mm);
43062306a36Sopenharmony_ci}
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_cistatic void hugetlb_free_pmd_range(struct mmu_gather *tlb, pud_t *pud,
43362306a36Sopenharmony_ci				   unsigned long addr, unsigned long end,
43462306a36Sopenharmony_ci				   unsigned long floor, unsigned long ceiling)
43562306a36Sopenharmony_ci{
43662306a36Sopenharmony_ci	pmd_t *pmd;
43762306a36Sopenharmony_ci	unsigned long next;
43862306a36Sopenharmony_ci	unsigned long start;
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	start = addr;
44162306a36Sopenharmony_ci	pmd = pmd_offset(pud, addr);
44262306a36Sopenharmony_ci	do {
44362306a36Sopenharmony_ci		next = pmd_addr_end(addr, end);
44462306a36Sopenharmony_ci		if (pmd_none(*pmd))
44562306a36Sopenharmony_ci			continue;
44662306a36Sopenharmony_ci		if (is_hugetlb_pmd(*pmd))
44762306a36Sopenharmony_ci			pmd_clear(pmd);
44862306a36Sopenharmony_ci		else
44962306a36Sopenharmony_ci			hugetlb_free_pte_range(tlb, pmd, addr);
45062306a36Sopenharmony_ci	} while (pmd++, addr = next, addr != end);
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	start &= PUD_MASK;
45362306a36Sopenharmony_ci	if (start < floor)
45462306a36Sopenharmony_ci		return;
45562306a36Sopenharmony_ci	if (ceiling) {
45662306a36Sopenharmony_ci		ceiling &= PUD_MASK;
45762306a36Sopenharmony_ci		if (!ceiling)
45862306a36Sopenharmony_ci			return;
45962306a36Sopenharmony_ci	}
46062306a36Sopenharmony_ci	if (end - 1 > ceiling - 1)
46162306a36Sopenharmony_ci		return;
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	pmd = pmd_offset(pud, start);
46462306a36Sopenharmony_ci	pud_clear(pud);
46562306a36Sopenharmony_ci	pmd_free_tlb(tlb, pmd, start);
46662306a36Sopenharmony_ci	mm_dec_nr_pmds(tlb->mm);
46762306a36Sopenharmony_ci}
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_cistatic void hugetlb_free_pud_range(struct mmu_gather *tlb, p4d_t *p4d,
47062306a36Sopenharmony_ci				   unsigned long addr, unsigned long end,
47162306a36Sopenharmony_ci				   unsigned long floor, unsigned long ceiling)
47262306a36Sopenharmony_ci{
47362306a36Sopenharmony_ci	pud_t *pud;
47462306a36Sopenharmony_ci	unsigned long next;
47562306a36Sopenharmony_ci	unsigned long start;
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	start = addr;
47862306a36Sopenharmony_ci	pud = pud_offset(p4d, addr);
47962306a36Sopenharmony_ci	do {
48062306a36Sopenharmony_ci		next = pud_addr_end(addr, end);
48162306a36Sopenharmony_ci		if (pud_none_or_clear_bad(pud))
48262306a36Sopenharmony_ci			continue;
48362306a36Sopenharmony_ci		if (is_hugetlb_pud(*pud))
48462306a36Sopenharmony_ci			pud_clear(pud);
48562306a36Sopenharmony_ci		else
48662306a36Sopenharmony_ci			hugetlb_free_pmd_range(tlb, pud, addr, next, floor,
48762306a36Sopenharmony_ci					       ceiling);
48862306a36Sopenharmony_ci	} while (pud++, addr = next, addr != end);
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	start &= PGDIR_MASK;
49162306a36Sopenharmony_ci	if (start < floor)
49262306a36Sopenharmony_ci		return;
49362306a36Sopenharmony_ci	if (ceiling) {
49462306a36Sopenharmony_ci		ceiling &= PGDIR_MASK;
49562306a36Sopenharmony_ci		if (!ceiling)
49662306a36Sopenharmony_ci			return;
49762306a36Sopenharmony_ci	}
49862306a36Sopenharmony_ci	if (end - 1 > ceiling - 1)
49962306a36Sopenharmony_ci		return;
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	pud = pud_offset(p4d, start);
50262306a36Sopenharmony_ci	p4d_clear(p4d);
50362306a36Sopenharmony_ci	pud_free_tlb(tlb, pud, start);
50462306a36Sopenharmony_ci	mm_dec_nr_puds(tlb->mm);
50562306a36Sopenharmony_ci}
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_civoid hugetlb_free_pgd_range(struct mmu_gather *tlb,
50862306a36Sopenharmony_ci			    unsigned long addr, unsigned long end,
50962306a36Sopenharmony_ci			    unsigned long floor, unsigned long ceiling)
51062306a36Sopenharmony_ci{
51162306a36Sopenharmony_ci	pgd_t *pgd;
51262306a36Sopenharmony_ci	p4d_t *p4d;
51362306a36Sopenharmony_ci	unsigned long next;
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	addr &= PMD_MASK;
51662306a36Sopenharmony_ci	if (addr < floor) {
51762306a36Sopenharmony_ci		addr += PMD_SIZE;
51862306a36Sopenharmony_ci		if (!addr)
51962306a36Sopenharmony_ci			return;
52062306a36Sopenharmony_ci	}
52162306a36Sopenharmony_ci	if (ceiling) {
52262306a36Sopenharmony_ci		ceiling &= PMD_MASK;
52362306a36Sopenharmony_ci		if (!ceiling)
52462306a36Sopenharmony_ci			return;
52562306a36Sopenharmony_ci	}
52662306a36Sopenharmony_ci	if (end - 1 > ceiling - 1)
52762306a36Sopenharmony_ci		end -= PMD_SIZE;
52862306a36Sopenharmony_ci	if (addr > end - 1)
52962306a36Sopenharmony_ci		return;
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	pgd = pgd_offset(tlb->mm, addr);
53262306a36Sopenharmony_ci	p4d = p4d_offset(pgd, addr);
53362306a36Sopenharmony_ci	do {
53462306a36Sopenharmony_ci		next = p4d_addr_end(addr, end);
53562306a36Sopenharmony_ci		if (p4d_none_or_clear_bad(p4d))
53662306a36Sopenharmony_ci			continue;
53762306a36Sopenharmony_ci		hugetlb_free_pud_range(tlb, p4d, addr, next, floor, ceiling);
53862306a36Sopenharmony_ci	} while (p4d++, addr = next, addr != end);
53962306a36Sopenharmony_ci}
540