162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * PPC64 Huge TLB Page Support for hash based MMUs (POWER4 and later)
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2003 David Gibson, IBM Corporation.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Based on the IA-32 version:
862306a36Sopenharmony_ci * Copyright (C) 2002, Rohit Seth <rohit.seth@intel.com>
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/mm.h>
1262306a36Sopenharmony_ci#include <linux/hugetlb.h>
1362306a36Sopenharmony_ci#include <asm/cacheflush.h>
1462306a36Sopenharmony_ci#include <asm/machdep.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ciunsigned int hpage_shift;
1762306a36Sopenharmony_ciEXPORT_SYMBOL(hpage_shift);
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#ifdef CONFIG_PPC_64S_HASH_MMU
2062306a36Sopenharmony_ciint __hash_page_huge(unsigned long ea, unsigned long access, unsigned long vsid,
2162306a36Sopenharmony_ci		     pte_t *ptep, unsigned long trap, unsigned long flags,
2262306a36Sopenharmony_ci		     int ssize, unsigned int shift, unsigned int mmu_psize)
2362306a36Sopenharmony_ci{
2462306a36Sopenharmony_ci	real_pte_t rpte;
2562306a36Sopenharmony_ci	unsigned long vpn;
2662306a36Sopenharmony_ci	unsigned long old_pte, new_pte;
2762306a36Sopenharmony_ci	unsigned long rflags, pa;
2862306a36Sopenharmony_ci	long slot, offset;
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci	BUG_ON(shift != mmu_psize_defs[mmu_psize].shift);
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci	/* Search the Linux page table for a match with va */
3362306a36Sopenharmony_ci	vpn = hpt_vpn(ea, vsid, ssize);
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	/*
3662306a36Sopenharmony_ci	 * At this point, we have a pte (old_pte) which can be used to build
3762306a36Sopenharmony_ci	 * or update an HPTE. There are 2 cases:
3862306a36Sopenharmony_ci	 *
3962306a36Sopenharmony_ci	 * 1. There is a valid (present) pte with no associated HPTE (this is
4062306a36Sopenharmony_ci	 *	the most common case)
4162306a36Sopenharmony_ci	 * 2. There is a valid (present) pte with an associated HPTE. The
4262306a36Sopenharmony_ci	 *	current values of the pp bits in the HPTE prevent access
4362306a36Sopenharmony_ci	 *	because we are doing software DIRTY bit management and the
4462306a36Sopenharmony_ci	 *	page is currently not DIRTY.
4562306a36Sopenharmony_ci	 */
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	do {
4962306a36Sopenharmony_ci		old_pte = pte_val(*ptep);
5062306a36Sopenharmony_ci		/* If PTE busy, retry the access */
5162306a36Sopenharmony_ci		if (unlikely(old_pte & H_PAGE_BUSY))
5262306a36Sopenharmony_ci			return 0;
5362306a36Sopenharmony_ci		/* If PTE permissions don't match, take page fault */
5462306a36Sopenharmony_ci		if (unlikely(!check_pte_access(access, old_pte)))
5562306a36Sopenharmony_ci			return 1;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci		/*
5862306a36Sopenharmony_ci		 * Try to lock the PTE, add ACCESSED and DIRTY if it was
5962306a36Sopenharmony_ci		 * a write access
6062306a36Sopenharmony_ci		 */
6162306a36Sopenharmony_ci		new_pte = old_pte | H_PAGE_BUSY | _PAGE_ACCESSED;
6262306a36Sopenharmony_ci		if (access & _PAGE_WRITE)
6362306a36Sopenharmony_ci			new_pte |= _PAGE_DIRTY;
6462306a36Sopenharmony_ci	} while(!pte_xchg(ptep, __pte(old_pte), __pte(new_pte)));
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	/* Make sure this is a hugetlb entry */
6762306a36Sopenharmony_ci	if (old_pte & (H_PAGE_THP_HUGE | _PAGE_DEVMAP))
6862306a36Sopenharmony_ci		return 0;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	rflags = htab_convert_pte_flags(new_pte, flags);
7162306a36Sopenharmony_ci	if (unlikely(mmu_psize == MMU_PAGE_16G))
7262306a36Sopenharmony_ci		offset = PTRS_PER_PUD;
7362306a36Sopenharmony_ci	else
7462306a36Sopenharmony_ci		offset = PTRS_PER_PMD;
7562306a36Sopenharmony_ci	rpte = __real_pte(__pte(old_pte), ptep, offset);
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	if (!cpu_has_feature(CPU_FTR_COHERENT_ICACHE))
7862306a36Sopenharmony_ci		/*
7962306a36Sopenharmony_ci		 * No CPU has hugepages but lacks no execute, so we
8062306a36Sopenharmony_ci		 * don't need to worry about that case
8162306a36Sopenharmony_ci		 */
8262306a36Sopenharmony_ci		rflags = hash_page_do_lazy_icache(rflags, __pte(old_pte), trap);
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	/* Check if pte already has an hpte (case 2) */
8562306a36Sopenharmony_ci	if (unlikely(old_pte & H_PAGE_HASHPTE)) {
8662306a36Sopenharmony_ci		/* There MIGHT be an HPTE for this pte */
8762306a36Sopenharmony_ci		unsigned long gslot;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci		gslot = pte_get_hash_gslot(vpn, shift, ssize, rpte, 0);
9062306a36Sopenharmony_ci		if (mmu_hash_ops.hpte_updatepp(gslot, rflags, vpn, mmu_psize,
9162306a36Sopenharmony_ci					       mmu_psize, ssize, flags) == -1)
9262306a36Sopenharmony_ci			old_pte &= ~_PAGE_HPTEFLAGS;
9362306a36Sopenharmony_ci	}
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	if (likely(!(old_pte & H_PAGE_HASHPTE))) {
9662306a36Sopenharmony_ci		unsigned long hash = hpt_hash(vpn, shift, ssize);
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci		pa = pte_pfn(__pte(old_pte)) << PAGE_SHIFT;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci		/* clear HPTE slot informations in new PTE */
10162306a36Sopenharmony_ci		new_pte = (new_pte & ~_PAGE_HPTEFLAGS) | H_PAGE_HASHPTE;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci		slot = hpte_insert_repeating(hash, vpn, pa, rflags, 0,
10462306a36Sopenharmony_ci					     mmu_psize, ssize);
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci		/*
10762306a36Sopenharmony_ci		 * Hypervisor failure. Restore old pte and return -1
10862306a36Sopenharmony_ci		 * similar to __hash_page_*
10962306a36Sopenharmony_ci		 */
11062306a36Sopenharmony_ci		if (unlikely(slot == -2)) {
11162306a36Sopenharmony_ci			*ptep = __pte(old_pte);
11262306a36Sopenharmony_ci			hash_failure_debug(ea, access, vsid, trap, ssize,
11362306a36Sopenharmony_ci					   mmu_psize, mmu_psize, old_pte);
11462306a36Sopenharmony_ci			return -1;
11562306a36Sopenharmony_ci		}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci		new_pte |= pte_set_hidx(ptep, rpte, 0, slot, offset);
11862306a36Sopenharmony_ci	}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	/*
12162306a36Sopenharmony_ci	 * No need to use ldarx/stdcx here
12262306a36Sopenharmony_ci	 */
12362306a36Sopenharmony_ci	*ptep = __pte(new_pte & ~H_PAGE_BUSY);
12462306a36Sopenharmony_ci	return 0;
12562306a36Sopenharmony_ci}
12662306a36Sopenharmony_ci#endif
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_cipte_t huge_ptep_modify_prot_start(struct vm_area_struct *vma,
12962306a36Sopenharmony_ci				  unsigned long addr, pte_t *ptep)
13062306a36Sopenharmony_ci{
13162306a36Sopenharmony_ci	unsigned long pte_val;
13262306a36Sopenharmony_ci	/*
13362306a36Sopenharmony_ci	 * Clear the _PAGE_PRESENT so that no hardware parallel update is
13462306a36Sopenharmony_ci	 * possible. Also keep the pte_present true so that we don't take
13562306a36Sopenharmony_ci	 * wrong fault.
13662306a36Sopenharmony_ci	 */
13762306a36Sopenharmony_ci	pte_val = pte_update(vma->vm_mm, addr, ptep,
13862306a36Sopenharmony_ci			     _PAGE_PRESENT, _PAGE_INVALID, 1);
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	return __pte(pte_val);
14162306a36Sopenharmony_ci}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_civoid huge_ptep_modify_prot_commit(struct vm_area_struct *vma, unsigned long addr,
14462306a36Sopenharmony_ci				  pte_t *ptep, pte_t old_pte, pte_t pte)
14562306a36Sopenharmony_ci{
14662306a36Sopenharmony_ci	unsigned long psize;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	if (radix_enabled())
14962306a36Sopenharmony_ci		return radix__huge_ptep_modify_prot_commit(vma, addr, ptep,
15062306a36Sopenharmony_ci							   old_pte, pte);
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	psize = huge_page_size(hstate_vma(vma));
15362306a36Sopenharmony_ci	set_huge_pte_at(vma->vm_mm, addr, ptep, pte, psize);
15462306a36Sopenharmony_ci}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_civoid __init hugetlbpage_init_defaultsize(void)
15762306a36Sopenharmony_ci{
15862306a36Sopenharmony_ci	/* Set default large page size. Currently, we pick 16M or 1M
15962306a36Sopenharmony_ci	 * depending on what is available
16062306a36Sopenharmony_ci	 */
16162306a36Sopenharmony_ci	if (mmu_psize_defs[MMU_PAGE_16M].shift)
16262306a36Sopenharmony_ci		hpage_shift = mmu_psize_defs[MMU_PAGE_16M].shift;
16362306a36Sopenharmony_ci	else if (mmu_psize_defs[MMU_PAGE_1M].shift)
16462306a36Sopenharmony_ci		hpage_shift = mmu_psize_defs[MMU_PAGE_1M].shift;
16562306a36Sopenharmony_ci	else if (mmu_psize_defs[MMU_PAGE_2M].shift)
16662306a36Sopenharmony_ci		hpage_shift = mmu_psize_defs[MMU_PAGE_2M].shift;
16762306a36Sopenharmony_ci}
168