18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * IA-32 Huge TLB Page Support for Kernel.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2002, Rohit Seth <rohit.seth@intel.com>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/init.h>
98c2ecf20Sopenharmony_ci#include <linux/fs.h>
108c2ecf20Sopenharmony_ci#include <linux/mm.h>
118c2ecf20Sopenharmony_ci#include <linux/sched/mm.h>
128c2ecf20Sopenharmony_ci#include <linux/hugetlb.h>
138c2ecf20Sopenharmony_ci#include <linux/pagemap.h>
148c2ecf20Sopenharmony_ci#include <linux/err.h>
158c2ecf20Sopenharmony_ci#include <linux/sysctl.h>
168c2ecf20Sopenharmony_ci#include <linux/compat.h>
178c2ecf20Sopenharmony_ci#include <asm/mman.h>
188c2ecf20Sopenharmony_ci#include <asm/tlb.h>
198c2ecf20Sopenharmony_ci#include <asm/tlbflush.h>
208c2ecf20Sopenharmony_ci#include <asm/elf.h>
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#if 0	/* This is just for testing */
238c2ecf20Sopenharmony_cistruct page *
248c2ecf20Sopenharmony_cifollow_huge_addr(struct mm_struct *mm, unsigned long address, int write)
258c2ecf20Sopenharmony_ci{
268c2ecf20Sopenharmony_ci	unsigned long start = address;
278c2ecf20Sopenharmony_ci	int length = 1;
288c2ecf20Sopenharmony_ci	int nr;
298c2ecf20Sopenharmony_ci	struct page *page;
308c2ecf20Sopenharmony_ci	struct vm_area_struct *vma;
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci	vma = find_vma(mm, addr);
338c2ecf20Sopenharmony_ci	if (!vma || !is_vm_hugetlb_page(vma))
348c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci	pte = huge_pte_offset(mm, address, vma_mmu_pagesize(vma));
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	/* hugetlb should be locked, and hence, prefaulted */
398c2ecf20Sopenharmony_ci	WARN_ON(!pte || pte_none(*pte));
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	page = &pte_page(*pte)[vpfn % (HPAGE_SIZE/PAGE_SIZE)];
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	WARN_ON(!PageHead(page));
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	return page;
468c2ecf20Sopenharmony_ci}
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ciint pmd_huge(pmd_t pmd)
498c2ecf20Sopenharmony_ci{
508c2ecf20Sopenharmony_ci	return 0;
518c2ecf20Sopenharmony_ci}
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ciint pud_huge(pud_t pud)
548c2ecf20Sopenharmony_ci{
558c2ecf20Sopenharmony_ci	return 0;
568c2ecf20Sopenharmony_ci}
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci#else
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci/*
618c2ecf20Sopenharmony_ci * pmd_huge() returns 1 if @pmd is hugetlb related entry, that is normal
628c2ecf20Sopenharmony_ci * hugetlb entry or non-present (migration or hwpoisoned) hugetlb entry.
638c2ecf20Sopenharmony_ci * Otherwise, returns 0.
648c2ecf20Sopenharmony_ci */
658c2ecf20Sopenharmony_ciint pmd_huge(pmd_t pmd)
668c2ecf20Sopenharmony_ci{
678c2ecf20Sopenharmony_ci	return !pmd_none(pmd) &&
688c2ecf20Sopenharmony_ci		(pmd_val(pmd) & (_PAGE_PRESENT|_PAGE_PSE)) != _PAGE_PRESENT;
698c2ecf20Sopenharmony_ci}
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ciint pud_huge(pud_t pud)
728c2ecf20Sopenharmony_ci{
738c2ecf20Sopenharmony_ci	return !!(pud_val(pud) & _PAGE_PSE);
748c2ecf20Sopenharmony_ci}
758c2ecf20Sopenharmony_ci#endif
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci#ifdef CONFIG_HUGETLB_PAGE
788c2ecf20Sopenharmony_cistatic unsigned long hugetlb_get_unmapped_area_bottomup(struct file *file,
798c2ecf20Sopenharmony_ci		unsigned long addr, unsigned long len,
808c2ecf20Sopenharmony_ci		unsigned long pgoff, unsigned long flags)
818c2ecf20Sopenharmony_ci{
828c2ecf20Sopenharmony_ci	struct hstate *h = hstate_file(file);
838c2ecf20Sopenharmony_ci	struct vm_unmapped_area_info info;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	info.flags = 0;
868c2ecf20Sopenharmony_ci	info.length = len;
878c2ecf20Sopenharmony_ci	info.low_limit = get_mmap_base(1);
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	/*
908c2ecf20Sopenharmony_ci	 * If hint address is above DEFAULT_MAP_WINDOW, look for unmapped area
918c2ecf20Sopenharmony_ci	 * in the full address space.
928c2ecf20Sopenharmony_ci	 */
938c2ecf20Sopenharmony_ci	info.high_limit = in_32bit_syscall() ?
948c2ecf20Sopenharmony_ci		task_size_32bit() : task_size_64bit(addr > DEFAULT_MAP_WINDOW);
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	info.align_mask = PAGE_MASK & ~huge_page_mask(h);
978c2ecf20Sopenharmony_ci	info.align_offset = 0;
988c2ecf20Sopenharmony_ci	return vm_unmapped_area(&info);
998c2ecf20Sopenharmony_ci}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_cistatic unsigned long hugetlb_get_unmapped_area_topdown(struct file *file,
1028c2ecf20Sopenharmony_ci		unsigned long addr, unsigned long len,
1038c2ecf20Sopenharmony_ci		unsigned long pgoff, unsigned long flags)
1048c2ecf20Sopenharmony_ci{
1058c2ecf20Sopenharmony_ci	struct hstate *h = hstate_file(file);
1068c2ecf20Sopenharmony_ci	struct vm_unmapped_area_info info;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	info.flags = VM_UNMAPPED_AREA_TOPDOWN;
1098c2ecf20Sopenharmony_ci	info.length = len;
1108c2ecf20Sopenharmony_ci	info.low_limit = PAGE_SIZE;
1118c2ecf20Sopenharmony_ci	info.high_limit = get_mmap_base(0);
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	/*
1148c2ecf20Sopenharmony_ci	 * If hint address is above DEFAULT_MAP_WINDOW, look for unmapped area
1158c2ecf20Sopenharmony_ci	 * in the full address space.
1168c2ecf20Sopenharmony_ci	 */
1178c2ecf20Sopenharmony_ci	if (addr > DEFAULT_MAP_WINDOW && !in_32bit_syscall())
1188c2ecf20Sopenharmony_ci		info.high_limit += TASK_SIZE_MAX - DEFAULT_MAP_WINDOW;
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	info.align_mask = PAGE_MASK & ~huge_page_mask(h);
1218c2ecf20Sopenharmony_ci	info.align_offset = 0;
1228c2ecf20Sopenharmony_ci	addr = vm_unmapped_area(&info);
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	/*
1258c2ecf20Sopenharmony_ci	 * A failed mmap() very likely causes application failure,
1268c2ecf20Sopenharmony_ci	 * so fall back to the bottom-up function here. This scenario
1278c2ecf20Sopenharmony_ci	 * can happen with large stack limits and large mmap()
1288c2ecf20Sopenharmony_ci	 * allocations.
1298c2ecf20Sopenharmony_ci	 */
1308c2ecf20Sopenharmony_ci	if (addr & ~PAGE_MASK) {
1318c2ecf20Sopenharmony_ci		VM_BUG_ON(addr != -ENOMEM);
1328c2ecf20Sopenharmony_ci		info.flags = 0;
1338c2ecf20Sopenharmony_ci		info.low_limit = TASK_UNMAPPED_BASE;
1348c2ecf20Sopenharmony_ci		info.high_limit = TASK_SIZE_LOW;
1358c2ecf20Sopenharmony_ci		addr = vm_unmapped_area(&info);
1368c2ecf20Sopenharmony_ci	}
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	return addr;
1398c2ecf20Sopenharmony_ci}
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ciunsigned long
1428c2ecf20Sopenharmony_cihugetlb_get_unmapped_area(struct file *file, unsigned long addr,
1438c2ecf20Sopenharmony_ci		unsigned long len, unsigned long pgoff, unsigned long flags)
1448c2ecf20Sopenharmony_ci{
1458c2ecf20Sopenharmony_ci	struct hstate *h = hstate_file(file);
1468c2ecf20Sopenharmony_ci	struct mm_struct *mm = current->mm;
1478c2ecf20Sopenharmony_ci	struct vm_area_struct *vma;
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	if (len & ~huge_page_mask(h))
1508c2ecf20Sopenharmony_ci		return -EINVAL;
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	if (len > TASK_SIZE)
1538c2ecf20Sopenharmony_ci		return -ENOMEM;
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	/* No address checking. See comment at mmap_address_hint_valid() */
1568c2ecf20Sopenharmony_ci	if (flags & MAP_FIXED) {
1578c2ecf20Sopenharmony_ci		if (prepare_hugepage_range(file, addr, len))
1588c2ecf20Sopenharmony_ci			return -EINVAL;
1598c2ecf20Sopenharmony_ci		return addr;
1608c2ecf20Sopenharmony_ci	}
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	if (addr) {
1638c2ecf20Sopenharmony_ci		addr &= huge_page_mask(h);
1648c2ecf20Sopenharmony_ci		if (!mmap_address_hint_valid(addr, len))
1658c2ecf20Sopenharmony_ci			goto get_unmapped_area;
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci		vma = find_vma(mm, addr);
1688c2ecf20Sopenharmony_ci		if (!vma || addr + len <= vm_start_gap(vma))
1698c2ecf20Sopenharmony_ci			return addr;
1708c2ecf20Sopenharmony_ci	}
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ciget_unmapped_area:
1738c2ecf20Sopenharmony_ci	if (mm->get_unmapped_area == arch_get_unmapped_area)
1748c2ecf20Sopenharmony_ci		return hugetlb_get_unmapped_area_bottomup(file, addr, len,
1758c2ecf20Sopenharmony_ci				pgoff, flags);
1768c2ecf20Sopenharmony_ci	else
1778c2ecf20Sopenharmony_ci		return hugetlb_get_unmapped_area_topdown(file, addr, len,
1788c2ecf20Sopenharmony_ci				pgoff, flags);
1798c2ecf20Sopenharmony_ci}
1808c2ecf20Sopenharmony_ci#endif /* CONFIG_HUGETLB_PAGE */
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci#ifdef CONFIG_X86_64
1838c2ecf20Sopenharmony_cibool __init arch_hugetlb_valid_size(unsigned long size)
1848c2ecf20Sopenharmony_ci{
1858c2ecf20Sopenharmony_ci	if (size == PMD_SIZE)
1868c2ecf20Sopenharmony_ci		return true;
1878c2ecf20Sopenharmony_ci	else if (size == PUD_SIZE && boot_cpu_has(X86_FEATURE_GBPAGES))
1888c2ecf20Sopenharmony_ci		return true;
1898c2ecf20Sopenharmony_ci	else
1908c2ecf20Sopenharmony_ci		return false;
1918c2ecf20Sopenharmony_ci}
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci#ifdef CONFIG_CONTIG_ALLOC
1948c2ecf20Sopenharmony_cistatic __init int gigantic_pages_init(void)
1958c2ecf20Sopenharmony_ci{
1968c2ecf20Sopenharmony_ci	/* With compaction or CMA we can allocate gigantic pages at runtime */
1978c2ecf20Sopenharmony_ci	if (boot_cpu_has(X86_FEATURE_GBPAGES))
1988c2ecf20Sopenharmony_ci		hugetlb_add_hstate(PUD_SHIFT - PAGE_SHIFT);
1998c2ecf20Sopenharmony_ci	return 0;
2008c2ecf20Sopenharmony_ci}
2018c2ecf20Sopenharmony_ciarch_initcall(gigantic_pages_init);
2028c2ecf20Sopenharmony_ci#endif
2038c2ecf20Sopenharmony_ci#endif
204