18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * PARISC64 Huge TLB page support.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * This parisc implementation is heavily based on the SPARC and x86 code.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Copyright (C) 2015 Helge Deller <deller@gmx.de>
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/fs.h>
118c2ecf20Sopenharmony_ci#include <linux/mm.h>
128c2ecf20Sopenharmony_ci#include <linux/sched/mm.h>
138c2ecf20Sopenharmony_ci#include <linux/hugetlb.h>
148c2ecf20Sopenharmony_ci#include <linux/pagemap.h>
158c2ecf20Sopenharmony_ci#include <linux/sysctl.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include <asm/mman.h>
188c2ecf20Sopenharmony_ci#include <asm/tlb.h>
198c2ecf20Sopenharmony_ci#include <asm/tlbflush.h>
208c2ecf20Sopenharmony_ci#include <asm/cacheflush.h>
218c2ecf20Sopenharmony_ci#include <asm/mmu_context.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ciunsigned long
258c2ecf20Sopenharmony_cihugetlb_get_unmapped_area(struct file *file, unsigned long addr,
268c2ecf20Sopenharmony_ci		unsigned long len, unsigned long pgoff, unsigned long flags)
278c2ecf20Sopenharmony_ci{
288c2ecf20Sopenharmony_ci	struct hstate *h = hstate_file(file);
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	if (len & ~huge_page_mask(h))
318c2ecf20Sopenharmony_ci		return -EINVAL;
328c2ecf20Sopenharmony_ci	if (len > TASK_SIZE)
338c2ecf20Sopenharmony_ci		return -ENOMEM;
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	if (flags & MAP_FIXED)
368c2ecf20Sopenharmony_ci		if (prepare_hugepage_range(file, addr, len))
378c2ecf20Sopenharmony_ci			return -EINVAL;
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	if (addr)
408c2ecf20Sopenharmony_ci		addr = ALIGN(addr, huge_page_size(h));
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	/* we need to make sure the colouring is OK */
438c2ecf20Sopenharmony_ci	return arch_get_unmapped_area(file, addr, len, pgoff, flags);
448c2ecf20Sopenharmony_ci}
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_cipte_t *huge_pte_alloc(struct mm_struct *mm,
488c2ecf20Sopenharmony_ci			unsigned long addr, unsigned long sz)
498c2ecf20Sopenharmony_ci{
508c2ecf20Sopenharmony_ci	pgd_t *pgd;
518c2ecf20Sopenharmony_ci	p4d_t *p4d;
528c2ecf20Sopenharmony_ci	pud_t *pud;
538c2ecf20Sopenharmony_ci	pmd_t *pmd;
548c2ecf20Sopenharmony_ci	pte_t *pte = NULL;
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	/* We must align the address, because our caller will run
578c2ecf20Sopenharmony_ci	 * set_huge_pte_at() on whatever we return, which writes out
588c2ecf20Sopenharmony_ci	 * all of the sub-ptes for the hugepage range.  So we have
598c2ecf20Sopenharmony_ci	 * to give it the first such sub-pte.
608c2ecf20Sopenharmony_ci	 */
618c2ecf20Sopenharmony_ci	addr &= HPAGE_MASK;
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	pgd = pgd_offset(mm, addr);
648c2ecf20Sopenharmony_ci	p4d = p4d_offset(pgd, addr);
658c2ecf20Sopenharmony_ci	pud = pud_alloc(mm, p4d, addr);
668c2ecf20Sopenharmony_ci	if (pud) {
678c2ecf20Sopenharmony_ci		pmd = pmd_alloc(mm, pud, addr);
688c2ecf20Sopenharmony_ci		if (pmd)
698c2ecf20Sopenharmony_ci			pte = pte_alloc_map(mm, pmd, addr);
708c2ecf20Sopenharmony_ci	}
718c2ecf20Sopenharmony_ci	return pte;
728c2ecf20Sopenharmony_ci}
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_cipte_t *huge_pte_offset(struct mm_struct *mm,
758c2ecf20Sopenharmony_ci		       unsigned long addr, unsigned long sz)
768c2ecf20Sopenharmony_ci{
778c2ecf20Sopenharmony_ci	pgd_t *pgd;
788c2ecf20Sopenharmony_ci	p4d_t *p4d;
798c2ecf20Sopenharmony_ci	pud_t *pud;
808c2ecf20Sopenharmony_ci	pmd_t *pmd;
818c2ecf20Sopenharmony_ci	pte_t *pte = NULL;
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	addr &= HPAGE_MASK;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	pgd = pgd_offset(mm, addr);
868c2ecf20Sopenharmony_ci	if (!pgd_none(*pgd)) {
878c2ecf20Sopenharmony_ci		p4d = p4d_offset(pgd, addr);
888c2ecf20Sopenharmony_ci		if (!p4d_none(*p4d)) {
898c2ecf20Sopenharmony_ci			pud = pud_offset(p4d, addr);
908c2ecf20Sopenharmony_ci			if (!pud_none(*pud)) {
918c2ecf20Sopenharmony_ci				pmd = pmd_offset(pud, addr);
928c2ecf20Sopenharmony_ci				if (!pmd_none(*pmd))
938c2ecf20Sopenharmony_ci					pte = pte_offset_map(pmd, addr);
948c2ecf20Sopenharmony_ci			}
958c2ecf20Sopenharmony_ci		}
968c2ecf20Sopenharmony_ci	}
978c2ecf20Sopenharmony_ci	return pte;
988c2ecf20Sopenharmony_ci}
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci/* Purge data and instruction TLB entries.  Must be called holding
1018c2ecf20Sopenharmony_ci * the pa_tlb_lock.  The TLB purge instructions are slow on SMP
1028c2ecf20Sopenharmony_ci * machines since the purge must be broadcast to all CPUs.
1038c2ecf20Sopenharmony_ci */
1048c2ecf20Sopenharmony_cistatic inline void purge_tlb_entries_huge(struct mm_struct *mm, unsigned long addr)
1058c2ecf20Sopenharmony_ci{
1068c2ecf20Sopenharmony_ci	int i;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	/* We may use multiple physical huge pages (e.g. 2x1 MB) to emulate
1098c2ecf20Sopenharmony_ci	 * Linux standard huge pages (e.g. 2 MB) */
1108c2ecf20Sopenharmony_ci	BUILD_BUG_ON(REAL_HPAGE_SHIFT > HPAGE_SHIFT);
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	addr &= HPAGE_MASK;
1138c2ecf20Sopenharmony_ci	addr |= _HUGE_PAGE_SIZE_ENCODING_DEFAULT;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	for (i = 0; i < (1 << (HPAGE_SHIFT-REAL_HPAGE_SHIFT)); i++) {
1168c2ecf20Sopenharmony_ci		purge_tlb_entries(mm, addr);
1178c2ecf20Sopenharmony_ci		addr += (1UL << REAL_HPAGE_SHIFT);
1188c2ecf20Sopenharmony_ci	}
1198c2ecf20Sopenharmony_ci}
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci/* __set_huge_pte_at() must be called holding the pa_tlb_lock. */
1228c2ecf20Sopenharmony_cistatic void __set_huge_pte_at(struct mm_struct *mm, unsigned long addr,
1238c2ecf20Sopenharmony_ci		     pte_t *ptep, pte_t entry)
1248c2ecf20Sopenharmony_ci{
1258c2ecf20Sopenharmony_ci	unsigned long addr_start;
1268c2ecf20Sopenharmony_ci	int i;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	addr &= HPAGE_MASK;
1298c2ecf20Sopenharmony_ci	addr_start = addr;
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	for (i = 0; i < (1 << HUGETLB_PAGE_ORDER); i++) {
1328c2ecf20Sopenharmony_ci		set_pte(ptep, entry);
1338c2ecf20Sopenharmony_ci		ptep++;
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci		addr += PAGE_SIZE;
1368c2ecf20Sopenharmony_ci		pte_val(entry) += PAGE_SIZE;
1378c2ecf20Sopenharmony_ci	}
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	purge_tlb_entries_huge(mm, addr_start);
1408c2ecf20Sopenharmony_ci}
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_civoid set_huge_pte_at(struct mm_struct *mm, unsigned long addr,
1438c2ecf20Sopenharmony_ci		     pte_t *ptep, pte_t entry)
1448c2ecf20Sopenharmony_ci{
1458c2ecf20Sopenharmony_ci	__set_huge_pte_at(mm, addr, ptep, entry);
1468c2ecf20Sopenharmony_ci}
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_cipte_t huge_ptep_get_and_clear(struct mm_struct *mm, unsigned long addr,
1508c2ecf20Sopenharmony_ci			      pte_t *ptep)
1518c2ecf20Sopenharmony_ci{
1528c2ecf20Sopenharmony_ci	pte_t entry;
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	entry = *ptep;
1558c2ecf20Sopenharmony_ci	__set_huge_pte_at(mm, addr, ptep, __pte(0));
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	return entry;
1588c2ecf20Sopenharmony_ci}
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_civoid huge_ptep_set_wrprotect(struct mm_struct *mm,
1628c2ecf20Sopenharmony_ci				unsigned long addr, pte_t *ptep)
1638c2ecf20Sopenharmony_ci{
1648c2ecf20Sopenharmony_ci	pte_t old_pte;
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	old_pte = *ptep;
1678c2ecf20Sopenharmony_ci	__set_huge_pte_at(mm, addr, ptep, pte_wrprotect(old_pte));
1688c2ecf20Sopenharmony_ci}
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ciint huge_ptep_set_access_flags(struct vm_area_struct *vma,
1718c2ecf20Sopenharmony_ci				unsigned long addr, pte_t *ptep,
1728c2ecf20Sopenharmony_ci				pte_t pte, int dirty)
1738c2ecf20Sopenharmony_ci{
1748c2ecf20Sopenharmony_ci	int changed;
1758c2ecf20Sopenharmony_ci	struct mm_struct *mm = vma->vm_mm;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	changed = !pte_same(*ptep, pte);
1788c2ecf20Sopenharmony_ci	if (changed) {
1798c2ecf20Sopenharmony_ci		__set_huge_pte_at(mm, addr, ptep, pte);
1808c2ecf20Sopenharmony_ci	}
1818c2ecf20Sopenharmony_ci	return changed;
1828c2ecf20Sopenharmony_ci}
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ciint pmd_huge(pmd_t pmd)
1868c2ecf20Sopenharmony_ci{
1878c2ecf20Sopenharmony_ci	return 0;
1888c2ecf20Sopenharmony_ci}
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ciint pud_huge(pud_t pud)
1918c2ecf20Sopenharmony_ci{
1928c2ecf20Sopenharmony_ci	return 0;
1938c2ecf20Sopenharmony_ci}
194