18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright 2005, Paul Mackerras, IBM Corporation.
48c2ecf20Sopenharmony_ci * Copyright 2009, Benjamin Herrenschmidt, IBM Corporation.
58c2ecf20Sopenharmony_ci * Copyright 2015-2016, Aneesh Kumar K.V, IBM Corporation.
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/sched.h>
98c2ecf20Sopenharmony_ci#include <linux/mm_types.h>
108c2ecf20Sopenharmony_ci#include <linux/mm.h>
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <asm/sections.h>
138c2ecf20Sopenharmony_ci#include <asm/mmu.h>
148c2ecf20Sopenharmony_ci#include <asm/tlb.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include <mm/mmu_decl.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#define CREATE_TRACE_POINTS
198c2ecf20Sopenharmony_ci#include <trace/events/thp.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#if H_PGTABLE_RANGE > (USER_VSID_RANGE * (TASK_SIZE_USER64 / TASK_CONTEXT_SIZE))
228c2ecf20Sopenharmony_ci#warning Limited user VSID range means pagetable space is wasted
238c2ecf20Sopenharmony_ci#endif
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#ifdef CONFIG_SPARSEMEM_VMEMMAP
268c2ecf20Sopenharmony_ci/*
278c2ecf20Sopenharmony_ci * vmemmap is the starting address of the virtual address space where
288c2ecf20Sopenharmony_ci * struct pages are allocated for all possible PFNs present on the system
298c2ecf20Sopenharmony_ci * including holes and bad memory (hence sparse). These virtual struct
308c2ecf20Sopenharmony_ci * pages are stored in sequence in this virtual address space irrespective
318c2ecf20Sopenharmony_ci * of the fact whether the corresponding PFN is valid or not. This achieves
328c2ecf20Sopenharmony_ci * constant relationship between address of struct page and its PFN.
338c2ecf20Sopenharmony_ci *
348c2ecf20Sopenharmony_ci * During boot or memory hotplug operation when a new memory section is
358c2ecf20Sopenharmony_ci * added, physical memory allocation (including hash table bolting) will
368c2ecf20Sopenharmony_ci * be performed for the set of struct pages which are part of the memory
378c2ecf20Sopenharmony_ci * section. This saves memory by not allocating struct pages for PFNs
388c2ecf20Sopenharmony_ci * which are not valid.
398c2ecf20Sopenharmony_ci *
408c2ecf20Sopenharmony_ci *		----------------------------------------------
418c2ecf20Sopenharmony_ci *		| PHYSICAL ALLOCATION OF VIRTUAL STRUCT PAGES|
428c2ecf20Sopenharmony_ci *		----------------------------------------------
438c2ecf20Sopenharmony_ci *
448c2ecf20Sopenharmony_ci *	   f000000000000000                  c000000000000000
458c2ecf20Sopenharmony_ci * vmemmap +--------------+                  +--------------+
468c2ecf20Sopenharmony_ci *  +      |  page struct | +--------------> |  page struct |
478c2ecf20Sopenharmony_ci *  |      +--------------+                  +--------------+
488c2ecf20Sopenharmony_ci *  |      |  page struct | +--------------> |  page struct |
498c2ecf20Sopenharmony_ci *  |      +--------------+ |                +--------------+
508c2ecf20Sopenharmony_ci *  |      |  page struct | +       +------> |  page struct |
518c2ecf20Sopenharmony_ci *  |      +--------------+         |        +--------------+
528c2ecf20Sopenharmony_ci *  |      |  page struct |         |   +--> |  page struct |
538c2ecf20Sopenharmony_ci *  |      +--------------+         |   |    +--------------+
548c2ecf20Sopenharmony_ci *  |      |  page struct |         |   |
558c2ecf20Sopenharmony_ci *  |      +--------------+         |   |
568c2ecf20Sopenharmony_ci *  |      |  page struct |         |   |
578c2ecf20Sopenharmony_ci *  |      +--------------+         |   |
588c2ecf20Sopenharmony_ci *  |      |  page struct |         |   |
598c2ecf20Sopenharmony_ci *  |      +--------------+         |   |
608c2ecf20Sopenharmony_ci *  |      |  page struct |         |   |
618c2ecf20Sopenharmony_ci *  |      +--------------+         |   |
628c2ecf20Sopenharmony_ci *  |      |  page struct | +-------+   |
638c2ecf20Sopenharmony_ci *  |      +--------------+             |
648c2ecf20Sopenharmony_ci *  |      |  page struct | +-----------+
658c2ecf20Sopenharmony_ci *  |      +--------------+
668c2ecf20Sopenharmony_ci *  |      |  page struct | No mapping
678c2ecf20Sopenharmony_ci *  |      +--------------+
688c2ecf20Sopenharmony_ci *  |      |  page struct | No mapping
698c2ecf20Sopenharmony_ci *  v      +--------------+
708c2ecf20Sopenharmony_ci *
718c2ecf20Sopenharmony_ci *		-----------------------------------------
728c2ecf20Sopenharmony_ci *		| RELATION BETWEEN STRUCT PAGES AND PFNS|
738c2ecf20Sopenharmony_ci *		-----------------------------------------
748c2ecf20Sopenharmony_ci *
758c2ecf20Sopenharmony_ci * vmemmap +--------------+                 +---------------+
768c2ecf20Sopenharmony_ci *  +      |  page struct | +-------------> |      PFN      |
778c2ecf20Sopenharmony_ci *  |      +--------------+                 +---------------+
788c2ecf20Sopenharmony_ci *  |      |  page struct | +-------------> |      PFN      |
798c2ecf20Sopenharmony_ci *  |      +--------------+                 +---------------+
808c2ecf20Sopenharmony_ci *  |      |  page struct | +-------------> |      PFN      |
818c2ecf20Sopenharmony_ci *  |      +--------------+                 +---------------+
828c2ecf20Sopenharmony_ci *  |      |  page struct | +-------------> |      PFN      |
838c2ecf20Sopenharmony_ci *  |      +--------------+                 +---------------+
848c2ecf20Sopenharmony_ci *  |      |              |
858c2ecf20Sopenharmony_ci *  |      +--------------+
868c2ecf20Sopenharmony_ci *  |      |              |
878c2ecf20Sopenharmony_ci *  |      +--------------+
888c2ecf20Sopenharmony_ci *  |      |              |
898c2ecf20Sopenharmony_ci *  |      +--------------+                 +---------------+
908c2ecf20Sopenharmony_ci *  |      |  page struct | +-------------> |      PFN      |
918c2ecf20Sopenharmony_ci *  |      +--------------+                 +---------------+
928c2ecf20Sopenharmony_ci *  |      |              |
938c2ecf20Sopenharmony_ci *  |      +--------------+
948c2ecf20Sopenharmony_ci *  |      |              |
958c2ecf20Sopenharmony_ci *  |      +--------------+                 +---------------+
968c2ecf20Sopenharmony_ci *  |      |  page struct | +-------------> |      PFN      |
978c2ecf20Sopenharmony_ci *  |      +--------------+                 +---------------+
988c2ecf20Sopenharmony_ci *  |      |  page struct | +-------------> |      PFN      |
998c2ecf20Sopenharmony_ci *  v      +--------------+                 +---------------+
1008c2ecf20Sopenharmony_ci */
1018c2ecf20Sopenharmony_ci/*
1028c2ecf20Sopenharmony_ci * On hash-based CPUs, the vmemmap is bolted in the hash table.
1038c2ecf20Sopenharmony_ci *
1048c2ecf20Sopenharmony_ci */
1058c2ecf20Sopenharmony_ciint __meminit hash__vmemmap_create_mapping(unsigned long start,
1068c2ecf20Sopenharmony_ci				       unsigned long page_size,
1078c2ecf20Sopenharmony_ci				       unsigned long phys)
1088c2ecf20Sopenharmony_ci{
1098c2ecf20Sopenharmony_ci	int rc;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	if ((start + page_size) >= H_VMEMMAP_END) {
1128c2ecf20Sopenharmony_ci		pr_warn("Outside the supported range\n");
1138c2ecf20Sopenharmony_ci		return -1;
1148c2ecf20Sopenharmony_ci	}
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	rc = htab_bolt_mapping(start, start + page_size, phys,
1178c2ecf20Sopenharmony_ci			       pgprot_val(PAGE_KERNEL),
1188c2ecf20Sopenharmony_ci			       mmu_vmemmap_psize, mmu_kernel_ssize);
1198c2ecf20Sopenharmony_ci	if (rc < 0) {
1208c2ecf20Sopenharmony_ci		int rc2 = htab_remove_mapping(start, start + page_size,
1218c2ecf20Sopenharmony_ci					      mmu_vmemmap_psize,
1228c2ecf20Sopenharmony_ci					      mmu_kernel_ssize);
1238c2ecf20Sopenharmony_ci		BUG_ON(rc2 && (rc2 != -ENOENT));
1248c2ecf20Sopenharmony_ci	}
1258c2ecf20Sopenharmony_ci	return rc;
1268c2ecf20Sopenharmony_ci}
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci#ifdef CONFIG_MEMORY_HOTPLUG
1298c2ecf20Sopenharmony_civoid hash__vmemmap_remove_mapping(unsigned long start,
1308c2ecf20Sopenharmony_ci			      unsigned long page_size)
1318c2ecf20Sopenharmony_ci{
1328c2ecf20Sopenharmony_ci	int rc = htab_remove_mapping(start, start + page_size,
1338c2ecf20Sopenharmony_ci				     mmu_vmemmap_psize,
1348c2ecf20Sopenharmony_ci				     mmu_kernel_ssize);
1358c2ecf20Sopenharmony_ci	BUG_ON((rc < 0) && (rc != -ENOENT));
1368c2ecf20Sopenharmony_ci	WARN_ON(rc == -ENOENT);
1378c2ecf20Sopenharmony_ci}
1388c2ecf20Sopenharmony_ci#endif
1398c2ecf20Sopenharmony_ci#endif /* CONFIG_SPARSEMEM_VMEMMAP */
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci/*
1428c2ecf20Sopenharmony_ci * map_kernel_page currently only called by __ioremap
1438c2ecf20Sopenharmony_ci * map_kernel_page adds an entry to the ioremap page table
1448c2ecf20Sopenharmony_ci * and adds an entry to the HPT, possibly bolting it
1458c2ecf20Sopenharmony_ci */
1468c2ecf20Sopenharmony_ciint hash__map_kernel_page(unsigned long ea, unsigned long pa, pgprot_t prot)
1478c2ecf20Sopenharmony_ci{
1488c2ecf20Sopenharmony_ci	pgd_t *pgdp;
1498c2ecf20Sopenharmony_ci	p4d_t *p4dp;
1508c2ecf20Sopenharmony_ci	pud_t *pudp;
1518c2ecf20Sopenharmony_ci	pmd_t *pmdp;
1528c2ecf20Sopenharmony_ci	pte_t *ptep;
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	BUILD_BUG_ON(TASK_SIZE_USER64 > H_PGTABLE_RANGE);
1558c2ecf20Sopenharmony_ci	if (slab_is_available()) {
1568c2ecf20Sopenharmony_ci		pgdp = pgd_offset_k(ea);
1578c2ecf20Sopenharmony_ci		p4dp = p4d_offset(pgdp, ea);
1588c2ecf20Sopenharmony_ci		pudp = pud_alloc(&init_mm, p4dp, ea);
1598c2ecf20Sopenharmony_ci		if (!pudp)
1608c2ecf20Sopenharmony_ci			return -ENOMEM;
1618c2ecf20Sopenharmony_ci		pmdp = pmd_alloc(&init_mm, pudp, ea);
1628c2ecf20Sopenharmony_ci		if (!pmdp)
1638c2ecf20Sopenharmony_ci			return -ENOMEM;
1648c2ecf20Sopenharmony_ci		ptep = pte_alloc_kernel(pmdp, ea);
1658c2ecf20Sopenharmony_ci		if (!ptep)
1668c2ecf20Sopenharmony_ci			return -ENOMEM;
1678c2ecf20Sopenharmony_ci		set_pte_at(&init_mm, ea, ptep, pfn_pte(pa >> PAGE_SHIFT, prot));
1688c2ecf20Sopenharmony_ci	} else {
1698c2ecf20Sopenharmony_ci		/*
1708c2ecf20Sopenharmony_ci		 * If the mm subsystem is not fully up, we cannot create a
1718c2ecf20Sopenharmony_ci		 * linux page table entry for this mapping.  Simply bolt an
1728c2ecf20Sopenharmony_ci		 * entry in the hardware page table.
1738c2ecf20Sopenharmony_ci		 *
1748c2ecf20Sopenharmony_ci		 */
1758c2ecf20Sopenharmony_ci		if (htab_bolt_mapping(ea, ea + PAGE_SIZE, pa, pgprot_val(prot),
1768c2ecf20Sopenharmony_ci				      mmu_io_psize, mmu_kernel_ssize)) {
1778c2ecf20Sopenharmony_ci			printk(KERN_ERR "Failed to do bolted mapping IO "
1788c2ecf20Sopenharmony_ci			       "memory at %016lx !\n", pa);
1798c2ecf20Sopenharmony_ci			return -ENOMEM;
1808c2ecf20Sopenharmony_ci		}
1818c2ecf20Sopenharmony_ci	}
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	smp_wmb();
1848c2ecf20Sopenharmony_ci	return 0;
1858c2ecf20Sopenharmony_ci}
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci#ifdef CONFIG_TRANSPARENT_HUGEPAGE
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ciunsigned long hash__pmd_hugepage_update(struct mm_struct *mm, unsigned long addr,
1908c2ecf20Sopenharmony_ci				    pmd_t *pmdp, unsigned long clr,
1918c2ecf20Sopenharmony_ci				    unsigned long set)
1928c2ecf20Sopenharmony_ci{
1938c2ecf20Sopenharmony_ci	__be64 old_be, tmp;
1948c2ecf20Sopenharmony_ci	unsigned long old;
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_VM
1978c2ecf20Sopenharmony_ci	WARN_ON(!hash__pmd_trans_huge(*pmdp) && !pmd_devmap(*pmdp));
1988c2ecf20Sopenharmony_ci	assert_spin_locked(pmd_lockptr(mm, pmdp));
1998c2ecf20Sopenharmony_ci#endif
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	__asm__ __volatile__(
2028c2ecf20Sopenharmony_ci	"1:	ldarx	%0,0,%3\n\
2038c2ecf20Sopenharmony_ci		and.	%1,%0,%6\n\
2048c2ecf20Sopenharmony_ci		bne-	1b \n\
2058c2ecf20Sopenharmony_ci		andc	%1,%0,%4 \n\
2068c2ecf20Sopenharmony_ci		or	%1,%1,%7\n\
2078c2ecf20Sopenharmony_ci		stdcx.	%1,0,%3 \n\
2088c2ecf20Sopenharmony_ci		bne-	1b"
2098c2ecf20Sopenharmony_ci	: "=&r" (old_be), "=&r" (tmp), "=m" (*pmdp)
2108c2ecf20Sopenharmony_ci	: "r" (pmdp), "r" (cpu_to_be64(clr)), "m" (*pmdp),
2118c2ecf20Sopenharmony_ci	  "r" (cpu_to_be64(H_PAGE_BUSY)), "r" (cpu_to_be64(set))
2128c2ecf20Sopenharmony_ci	: "cc" );
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	old = be64_to_cpu(old_be);
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	trace_hugepage_update(addr, old, clr, set);
2178c2ecf20Sopenharmony_ci	if (old & H_PAGE_HASHPTE)
2188c2ecf20Sopenharmony_ci		hpte_do_hugepage_flush(mm, addr, pmdp, old);
2198c2ecf20Sopenharmony_ci	return old;
2208c2ecf20Sopenharmony_ci}
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_cipmd_t hash__pmdp_collapse_flush(struct vm_area_struct *vma, unsigned long address,
2238c2ecf20Sopenharmony_ci			    pmd_t *pmdp)
2248c2ecf20Sopenharmony_ci{
2258c2ecf20Sopenharmony_ci	pmd_t pmd;
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	VM_BUG_ON(address & ~HPAGE_PMD_MASK);
2288c2ecf20Sopenharmony_ci	VM_BUG_ON(pmd_trans_huge(*pmdp));
2298c2ecf20Sopenharmony_ci	VM_BUG_ON(pmd_devmap(*pmdp));
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	pmd = *pmdp;
2328c2ecf20Sopenharmony_ci	pmd_clear(pmdp);
2338c2ecf20Sopenharmony_ci	/*
2348c2ecf20Sopenharmony_ci	 * Wait for all pending hash_page to finish. This is needed
2358c2ecf20Sopenharmony_ci	 * in case of subpage collapse. When we collapse normal pages
2368c2ecf20Sopenharmony_ci	 * to hugepage, we first clear the pmd, then invalidate all
2378c2ecf20Sopenharmony_ci	 * the PTE entries. The assumption here is that any low level
2388c2ecf20Sopenharmony_ci	 * page fault will see a none pmd and take the slow path that
2398c2ecf20Sopenharmony_ci	 * will wait on mmap_lock. But we could very well be in a
2408c2ecf20Sopenharmony_ci	 * hash_page with local ptep pointer value. Such a hash page
2418c2ecf20Sopenharmony_ci	 * can result in adding new HPTE entries for normal subpages.
2428c2ecf20Sopenharmony_ci	 * That means we could be modifying the page content as we
2438c2ecf20Sopenharmony_ci	 * copy them to a huge page. So wait for parallel hash_page
2448c2ecf20Sopenharmony_ci	 * to finish before invalidating HPTE entries. We can do this
2458c2ecf20Sopenharmony_ci	 * by sending an IPI to all the cpus and executing a dummy
2468c2ecf20Sopenharmony_ci	 * function there.
2478c2ecf20Sopenharmony_ci	 */
2488c2ecf20Sopenharmony_ci	serialize_against_pte_lookup(vma->vm_mm);
2498c2ecf20Sopenharmony_ci	/*
2508c2ecf20Sopenharmony_ci	 * Now invalidate the hpte entries in the range
2518c2ecf20Sopenharmony_ci	 * covered by pmd. This make sure we take a
2528c2ecf20Sopenharmony_ci	 * fault and will find the pmd as none, which will
2538c2ecf20Sopenharmony_ci	 * result in a major fault which takes mmap_lock and
2548c2ecf20Sopenharmony_ci	 * hence wait for collapse to complete. Without this
2558c2ecf20Sopenharmony_ci	 * the __collapse_huge_page_copy can result in copying
2568c2ecf20Sopenharmony_ci	 * the old content.
2578c2ecf20Sopenharmony_ci	 */
2588c2ecf20Sopenharmony_ci	flush_tlb_pmd_range(vma->vm_mm, &pmd, address);
2598c2ecf20Sopenharmony_ci	return pmd;
2608c2ecf20Sopenharmony_ci}
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci/*
2638c2ecf20Sopenharmony_ci * We want to put the pgtable in pmd and use pgtable for tracking
2648c2ecf20Sopenharmony_ci * the base page size hptes
2658c2ecf20Sopenharmony_ci */
2668c2ecf20Sopenharmony_civoid hash__pgtable_trans_huge_deposit(struct mm_struct *mm, pmd_t *pmdp,
2678c2ecf20Sopenharmony_ci				  pgtable_t pgtable)
2688c2ecf20Sopenharmony_ci{
2698c2ecf20Sopenharmony_ci	pgtable_t *pgtable_slot;
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	assert_spin_locked(pmd_lockptr(mm, pmdp));
2728c2ecf20Sopenharmony_ci	/*
2738c2ecf20Sopenharmony_ci	 * we store the pgtable in the second half of PMD
2748c2ecf20Sopenharmony_ci	 */
2758c2ecf20Sopenharmony_ci	pgtable_slot = (pgtable_t *)pmdp + PTRS_PER_PMD;
2768c2ecf20Sopenharmony_ci	*pgtable_slot = pgtable;
2778c2ecf20Sopenharmony_ci	/*
2788c2ecf20Sopenharmony_ci	 * expose the deposited pgtable to other cpus.
2798c2ecf20Sopenharmony_ci	 * before we set the hugepage PTE at pmd level
2808c2ecf20Sopenharmony_ci	 * hash fault code looks at the deposted pgtable
2818c2ecf20Sopenharmony_ci	 * to store hash index values.
2828c2ecf20Sopenharmony_ci	 */
2838c2ecf20Sopenharmony_ci	smp_wmb();
2848c2ecf20Sopenharmony_ci}
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_cipgtable_t hash__pgtable_trans_huge_withdraw(struct mm_struct *mm, pmd_t *pmdp)
2878c2ecf20Sopenharmony_ci{
2888c2ecf20Sopenharmony_ci	pgtable_t pgtable;
2898c2ecf20Sopenharmony_ci	pgtable_t *pgtable_slot;
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	assert_spin_locked(pmd_lockptr(mm, pmdp));
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	pgtable_slot = (pgtable_t *)pmdp + PTRS_PER_PMD;
2948c2ecf20Sopenharmony_ci	pgtable = *pgtable_slot;
2958c2ecf20Sopenharmony_ci	/*
2968c2ecf20Sopenharmony_ci	 * Once we withdraw, mark the entry NULL.
2978c2ecf20Sopenharmony_ci	 */
2988c2ecf20Sopenharmony_ci	*pgtable_slot = NULL;
2998c2ecf20Sopenharmony_ci	/*
3008c2ecf20Sopenharmony_ci	 * We store HPTE information in the deposited PTE fragment.
3018c2ecf20Sopenharmony_ci	 * zero out the content on withdraw.
3028c2ecf20Sopenharmony_ci	 */
3038c2ecf20Sopenharmony_ci	memset(pgtable, 0, PTE_FRAG_SIZE);
3048c2ecf20Sopenharmony_ci	return pgtable;
3058c2ecf20Sopenharmony_ci}
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci/*
3088c2ecf20Sopenharmony_ci * A linux hugepage PMD was changed and the corresponding hash table entries
3098c2ecf20Sopenharmony_ci * neesd to be flushed.
3108c2ecf20Sopenharmony_ci */
3118c2ecf20Sopenharmony_civoid hpte_do_hugepage_flush(struct mm_struct *mm, unsigned long addr,
3128c2ecf20Sopenharmony_ci			    pmd_t *pmdp, unsigned long old_pmd)
3138c2ecf20Sopenharmony_ci{
3148c2ecf20Sopenharmony_ci	int ssize;
3158c2ecf20Sopenharmony_ci	unsigned int psize;
3168c2ecf20Sopenharmony_ci	unsigned long vsid;
3178c2ecf20Sopenharmony_ci	unsigned long flags = 0;
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	/* get the base page size,vsid and segment size */
3208c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_VM
3218c2ecf20Sopenharmony_ci	psize = get_slice_psize(mm, addr);
3228c2ecf20Sopenharmony_ci	BUG_ON(psize == MMU_PAGE_16M);
3238c2ecf20Sopenharmony_ci#endif
3248c2ecf20Sopenharmony_ci	if (old_pmd & H_PAGE_COMBO)
3258c2ecf20Sopenharmony_ci		psize = MMU_PAGE_4K;
3268c2ecf20Sopenharmony_ci	else
3278c2ecf20Sopenharmony_ci		psize = MMU_PAGE_64K;
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	if (!is_kernel_addr(addr)) {
3308c2ecf20Sopenharmony_ci		ssize = user_segment_size(addr);
3318c2ecf20Sopenharmony_ci		vsid = get_user_vsid(&mm->context, addr, ssize);
3328c2ecf20Sopenharmony_ci		WARN_ON(vsid == 0);
3338c2ecf20Sopenharmony_ci	} else {
3348c2ecf20Sopenharmony_ci		vsid = get_kernel_vsid(addr, mmu_kernel_ssize);
3358c2ecf20Sopenharmony_ci		ssize = mmu_kernel_ssize;
3368c2ecf20Sopenharmony_ci	}
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	if (mm_is_thread_local(mm))
3398c2ecf20Sopenharmony_ci		flags |= HPTE_LOCAL_UPDATE;
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	return flush_hash_hugepage(vsid, addr, pmdp, psize, ssize, flags);
3428c2ecf20Sopenharmony_ci}
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_cipmd_t hash__pmdp_huge_get_and_clear(struct mm_struct *mm,
3458c2ecf20Sopenharmony_ci				unsigned long addr, pmd_t *pmdp)
3468c2ecf20Sopenharmony_ci{
3478c2ecf20Sopenharmony_ci	pmd_t old_pmd;
3488c2ecf20Sopenharmony_ci	pgtable_t pgtable;
3498c2ecf20Sopenharmony_ci	unsigned long old;
3508c2ecf20Sopenharmony_ci	pgtable_t *pgtable_slot;
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	old = pmd_hugepage_update(mm, addr, pmdp, ~0UL, 0);
3538c2ecf20Sopenharmony_ci	old_pmd = __pmd(old);
3548c2ecf20Sopenharmony_ci	/*
3558c2ecf20Sopenharmony_ci	 * We have pmd == none and we are holding page_table_lock.
3568c2ecf20Sopenharmony_ci	 * So we can safely go and clear the pgtable hash
3578c2ecf20Sopenharmony_ci	 * index info.
3588c2ecf20Sopenharmony_ci	 */
3598c2ecf20Sopenharmony_ci	pgtable_slot = (pgtable_t *)pmdp + PTRS_PER_PMD;
3608c2ecf20Sopenharmony_ci	pgtable = *pgtable_slot;
3618c2ecf20Sopenharmony_ci	/*
3628c2ecf20Sopenharmony_ci	 * Let's zero out old valid and hash index details
3638c2ecf20Sopenharmony_ci	 * hash fault look at them.
3648c2ecf20Sopenharmony_ci	 */
3658c2ecf20Sopenharmony_ci	memset(pgtable, 0, PTE_FRAG_SIZE);
3668c2ecf20Sopenharmony_ci	return old_pmd;
3678c2ecf20Sopenharmony_ci}
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ciint hash__has_transparent_hugepage(void)
3708c2ecf20Sopenharmony_ci{
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	if (!mmu_has_feature(MMU_FTR_16M_PAGE))
3738c2ecf20Sopenharmony_ci		return 0;
3748c2ecf20Sopenharmony_ci	/*
3758c2ecf20Sopenharmony_ci	 * We support THP only if PMD_SIZE is 16MB.
3768c2ecf20Sopenharmony_ci	 */
3778c2ecf20Sopenharmony_ci	if (mmu_psize_defs[MMU_PAGE_16M].shift != PMD_SHIFT)
3788c2ecf20Sopenharmony_ci		return 0;
3798c2ecf20Sopenharmony_ci	/*
3808c2ecf20Sopenharmony_ci	 * We need to make sure that we support 16MB hugepage in a segement
3818c2ecf20Sopenharmony_ci	 * with base page size 64K or 4K. We only enable THP with a PAGE_SIZE
3828c2ecf20Sopenharmony_ci	 * of 64K.
3838c2ecf20Sopenharmony_ci	 */
3848c2ecf20Sopenharmony_ci	/*
3858c2ecf20Sopenharmony_ci	 * If we have 64K HPTE, we will be using that by default
3868c2ecf20Sopenharmony_ci	 */
3878c2ecf20Sopenharmony_ci	if (mmu_psize_defs[MMU_PAGE_64K].shift &&
3888c2ecf20Sopenharmony_ci	    (mmu_psize_defs[MMU_PAGE_64K].penc[MMU_PAGE_16M] == -1))
3898c2ecf20Sopenharmony_ci		return 0;
3908c2ecf20Sopenharmony_ci	/*
3918c2ecf20Sopenharmony_ci	 * Ok we only have 4K HPTE
3928c2ecf20Sopenharmony_ci	 */
3938c2ecf20Sopenharmony_ci	if (mmu_psize_defs[MMU_PAGE_4K].penc[MMU_PAGE_16M] == -1)
3948c2ecf20Sopenharmony_ci		return 0;
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	return 1;
3978c2ecf20Sopenharmony_ci}
3988c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(hash__has_transparent_hugepage);
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci#endif /* CONFIG_TRANSPARENT_HUGEPAGE */
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci#ifdef CONFIG_STRICT_KERNEL_RWX
4038c2ecf20Sopenharmony_cistatic bool hash__change_memory_range(unsigned long start, unsigned long end,
4048c2ecf20Sopenharmony_ci				      unsigned long newpp)
4058c2ecf20Sopenharmony_ci{
4068c2ecf20Sopenharmony_ci	unsigned long idx;
4078c2ecf20Sopenharmony_ci	unsigned int step, shift;
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	shift = mmu_psize_defs[mmu_linear_psize].shift;
4108c2ecf20Sopenharmony_ci	step = 1 << shift;
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci	start = ALIGN_DOWN(start, step);
4138c2ecf20Sopenharmony_ci	end = ALIGN(end, step); // aligns up
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	if (start >= end)
4168c2ecf20Sopenharmony_ci		return false;
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	pr_debug("Changing page protection on range 0x%lx-0x%lx, to 0x%lx, step 0x%x\n",
4198c2ecf20Sopenharmony_ci		 start, end, newpp, step);
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	for (idx = start; idx < end; idx += step)
4228c2ecf20Sopenharmony_ci		/* Not sure if we can do much with the return value */
4238c2ecf20Sopenharmony_ci		mmu_hash_ops.hpte_updateboltedpp(newpp, idx, mmu_linear_psize,
4248c2ecf20Sopenharmony_ci							mmu_kernel_ssize);
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci	return true;
4278c2ecf20Sopenharmony_ci}
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_civoid hash__mark_rodata_ro(void)
4308c2ecf20Sopenharmony_ci{
4318c2ecf20Sopenharmony_ci	unsigned long start, end;
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci	start = (unsigned long)_stext;
4348c2ecf20Sopenharmony_ci	end = (unsigned long)__init_begin;
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci	WARN_ON(!hash__change_memory_range(start, end, PP_RXXX));
4378c2ecf20Sopenharmony_ci}
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_civoid hash__mark_initmem_nx(void)
4408c2ecf20Sopenharmony_ci{
4418c2ecf20Sopenharmony_ci	unsigned long start, end, pp;
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci	start = (unsigned long)__init_begin;
4448c2ecf20Sopenharmony_ci	end = (unsigned long)__init_end;
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	pp = htab_convert_pte_flags(pgprot_val(PAGE_KERNEL));
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	WARN_ON(!hash__change_memory_range(start, end, pp));
4498c2ecf20Sopenharmony_ci}
4508c2ecf20Sopenharmony_ci#endif
451