162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */
262306a36Sopenharmony_ci#ifndef _ASM_POWERPC_BOOK3S_64_HUGETLB_H
362306a36Sopenharmony_ci#define _ASM_POWERPC_BOOK3S_64_HUGETLB_H
462306a36Sopenharmony_ci
562306a36Sopenharmony_ci#include <asm/firmware.h>
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci/*
862306a36Sopenharmony_ci * For radix we want generic code to handle hugetlb. But then if we want
962306a36Sopenharmony_ci * both hash and radix to be enabled together we need to workaround the
1062306a36Sopenharmony_ci * limitations.
1162306a36Sopenharmony_ci */
1262306a36Sopenharmony_civoid radix__flush_hugetlb_page(struct vm_area_struct *vma, unsigned long vmaddr);
1362306a36Sopenharmony_civoid radix__local_flush_hugetlb_page(struct vm_area_struct *vma, unsigned long vmaddr);
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ciextern void radix__huge_ptep_modify_prot_commit(struct vm_area_struct *vma,
1662306a36Sopenharmony_ci						unsigned long addr, pte_t *ptep,
1762306a36Sopenharmony_ci						pte_t old_pte, pte_t pte);
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_cistatic inline int hstate_get_psize(struct hstate *hstate)
2062306a36Sopenharmony_ci{
2162306a36Sopenharmony_ci	unsigned long shift;
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci	shift = huge_page_shift(hstate);
2462306a36Sopenharmony_ci	if (shift == mmu_psize_defs[MMU_PAGE_2M].shift)
2562306a36Sopenharmony_ci		return MMU_PAGE_2M;
2662306a36Sopenharmony_ci	else if (shift == mmu_psize_defs[MMU_PAGE_1G].shift)
2762306a36Sopenharmony_ci		return MMU_PAGE_1G;
2862306a36Sopenharmony_ci	else if (shift == mmu_psize_defs[MMU_PAGE_16M].shift)
2962306a36Sopenharmony_ci		return MMU_PAGE_16M;
3062306a36Sopenharmony_ci	else if (shift == mmu_psize_defs[MMU_PAGE_16G].shift)
3162306a36Sopenharmony_ci		return MMU_PAGE_16G;
3262306a36Sopenharmony_ci	else {
3362306a36Sopenharmony_ci		WARN(1, "Wrong huge page shift\n");
3462306a36Sopenharmony_ci		return mmu_virtual_psize;
3562306a36Sopenharmony_ci	}
3662306a36Sopenharmony_ci}
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci#define __HAVE_ARCH_GIGANTIC_PAGE_RUNTIME_SUPPORTED
3962306a36Sopenharmony_cistatic inline bool gigantic_page_runtime_supported(void)
4062306a36Sopenharmony_ci{
4162306a36Sopenharmony_ci	/*
4262306a36Sopenharmony_ci	 * We used gigantic page reservation with hypervisor assist in some case.
4362306a36Sopenharmony_ci	 * We cannot use runtime allocation of gigantic pages in those platforms
4462306a36Sopenharmony_ci	 * This is hash translation mode LPARs.
4562306a36Sopenharmony_ci	 */
4662306a36Sopenharmony_ci	if (firmware_has_feature(FW_FEATURE_LPAR) && !radix_enabled())
4762306a36Sopenharmony_ci		return false;
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	return true;
5062306a36Sopenharmony_ci}
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci/* hugepd entry valid bit */
5362306a36Sopenharmony_ci#define HUGEPD_VAL_BITS		(0x8000000000000000UL)
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci#define huge_ptep_modify_prot_start huge_ptep_modify_prot_start
5662306a36Sopenharmony_ciextern pte_t huge_ptep_modify_prot_start(struct vm_area_struct *vma,
5762306a36Sopenharmony_ci					 unsigned long addr, pte_t *ptep);
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci#define huge_ptep_modify_prot_commit huge_ptep_modify_prot_commit
6062306a36Sopenharmony_ciextern void huge_ptep_modify_prot_commit(struct vm_area_struct *vma,
6162306a36Sopenharmony_ci					 unsigned long addr, pte_t *ptep,
6262306a36Sopenharmony_ci					 pte_t old_pte, pte_t new_pte);
6362306a36Sopenharmony_ci/*
6462306a36Sopenharmony_ci * This should work for other subarchs too. But right now we use the
6562306a36Sopenharmony_ci * new format only for 64bit book3s
6662306a36Sopenharmony_ci */
6762306a36Sopenharmony_cistatic inline pte_t *hugepd_page(hugepd_t hpd)
6862306a36Sopenharmony_ci{
6962306a36Sopenharmony_ci	BUG_ON(!hugepd_ok(hpd));
7062306a36Sopenharmony_ci	/*
7162306a36Sopenharmony_ci	 * We have only four bits to encode, MMU page size
7262306a36Sopenharmony_ci	 */
7362306a36Sopenharmony_ci	BUILD_BUG_ON((MMU_PAGE_COUNT - 1) > 0xf);
7462306a36Sopenharmony_ci	return __va(hpd_val(hpd) & HUGEPD_ADDR_MASK);
7562306a36Sopenharmony_ci}
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_cistatic inline unsigned int hugepd_mmu_psize(hugepd_t hpd)
7862306a36Sopenharmony_ci{
7962306a36Sopenharmony_ci	return (hpd_val(hpd) & HUGEPD_SHIFT_MASK) >> 2;
8062306a36Sopenharmony_ci}
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_cistatic inline unsigned int hugepd_shift(hugepd_t hpd)
8362306a36Sopenharmony_ci{
8462306a36Sopenharmony_ci	return mmu_psize_to_shift(hugepd_mmu_psize(hpd));
8562306a36Sopenharmony_ci}
8662306a36Sopenharmony_cistatic inline void flush_hugetlb_page(struct vm_area_struct *vma,
8762306a36Sopenharmony_ci				      unsigned long vmaddr)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	if (radix_enabled())
9062306a36Sopenharmony_ci		return radix__flush_hugetlb_page(vma, vmaddr);
9162306a36Sopenharmony_ci}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_cistatic inline pte_t *hugepte_offset(hugepd_t hpd, unsigned long addr,
9462306a36Sopenharmony_ci				    unsigned int pdshift)
9562306a36Sopenharmony_ci{
9662306a36Sopenharmony_ci	unsigned long idx = (addr & ((1UL << pdshift) - 1)) >> hugepd_shift(hpd);
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	return hugepd_page(hpd) + idx;
9962306a36Sopenharmony_ci}
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_cistatic inline void hugepd_populate(hugepd_t *hpdp, pte_t *new, unsigned int pshift)
10262306a36Sopenharmony_ci{
10362306a36Sopenharmony_ci	*hpdp = __hugepd(__pa(new) | HUGEPD_VAL_BITS | (shift_to_mmu_psize(pshift) << 2));
10462306a36Sopenharmony_ci}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_civoid flush_hugetlb_page(struct vm_area_struct *vma, unsigned long vmaddr);
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_cistatic inline int check_and_get_huge_psize(int shift)
10962306a36Sopenharmony_ci{
11062306a36Sopenharmony_ci	int mmu_psize;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	if (shift > SLICE_HIGH_SHIFT)
11362306a36Sopenharmony_ci		return -EINVAL;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	mmu_psize = shift_to_mmu_psize(shift);
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	/*
11862306a36Sopenharmony_ci	 * We need to make sure that for different page sizes reported by
11962306a36Sopenharmony_ci	 * firmware we only add hugetlb support for page sizes that can be
12062306a36Sopenharmony_ci	 * supported by linux page table layout.
12162306a36Sopenharmony_ci	 * For now we have
12262306a36Sopenharmony_ci	 * Radix: 2M and 1G
12362306a36Sopenharmony_ci	 * Hash: 16M and 16G
12462306a36Sopenharmony_ci	 */
12562306a36Sopenharmony_ci	if (radix_enabled()) {
12662306a36Sopenharmony_ci		if (mmu_psize != MMU_PAGE_2M && mmu_psize != MMU_PAGE_1G)
12762306a36Sopenharmony_ci			return -EINVAL;
12862306a36Sopenharmony_ci	} else {
12962306a36Sopenharmony_ci		if (mmu_psize != MMU_PAGE_16M && mmu_psize != MMU_PAGE_16G)
13062306a36Sopenharmony_ci			return -EINVAL;
13162306a36Sopenharmony_ci	}
13262306a36Sopenharmony_ci	return mmu_psize;
13362306a36Sopenharmony_ci}
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci#endif
136