18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * native hashtable management.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * SMP scalability work:
68c2ecf20Sopenharmony_ci *    Copyright (C) 2001 Anton Blanchard <anton@au.ibm.com>, IBM
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#undef DEBUG_LOW
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
128c2ecf20Sopenharmony_ci#include <linux/bitops.h>
138c2ecf20Sopenharmony_ci#include <linux/of.h>
148c2ecf20Sopenharmony_ci#include <linux/processor.h>
158c2ecf20Sopenharmony_ci#include <linux/threads.h>
168c2ecf20Sopenharmony_ci#include <linux/smp.h>
178c2ecf20Sopenharmony_ci#include <linux/pgtable.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#include <asm/machdep.h>
208c2ecf20Sopenharmony_ci#include <asm/mmu.h>
218c2ecf20Sopenharmony_ci#include <asm/mmu_context.h>
228c2ecf20Sopenharmony_ci#include <asm/trace.h>
238c2ecf20Sopenharmony_ci#include <asm/tlb.h>
248c2ecf20Sopenharmony_ci#include <asm/cputable.h>
258c2ecf20Sopenharmony_ci#include <asm/udbg.h>
268c2ecf20Sopenharmony_ci#include <asm/kexec.h>
278c2ecf20Sopenharmony_ci#include <asm/ppc-opcode.h>
288c2ecf20Sopenharmony_ci#include <asm/feature-fixups.h>
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#include <misc/cxl-base.h>
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci#ifdef DEBUG_LOW
338c2ecf20Sopenharmony_ci#define DBG_LOW(fmt...) udbg_printf(fmt)
348c2ecf20Sopenharmony_ci#else
358c2ecf20Sopenharmony_ci#define DBG_LOW(fmt...)
368c2ecf20Sopenharmony_ci#endif
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci#ifdef __BIG_ENDIAN__
398c2ecf20Sopenharmony_ci#define HPTE_LOCK_BIT 3
408c2ecf20Sopenharmony_ci#else
418c2ecf20Sopenharmony_ci#define HPTE_LOCK_BIT (56+3)
428c2ecf20Sopenharmony_ci#endif
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_cistatic DEFINE_RAW_SPINLOCK(native_tlbie_lock);
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_cistatic inline void tlbiel_hash_set_isa206(unsigned int set, unsigned int is)
478c2ecf20Sopenharmony_ci{
488c2ecf20Sopenharmony_ci	unsigned long rb;
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	rb = (set << PPC_BITLSHIFT(51)) | (is << PPC_BITLSHIFT(53));
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	asm volatile("tlbiel %0" : : "r" (rb));
538c2ecf20Sopenharmony_ci}
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci/*
568c2ecf20Sopenharmony_ci * tlbiel instruction for hash, set invalidation
578c2ecf20Sopenharmony_ci * i.e., r=1 and is=01 or is=10 or is=11
588c2ecf20Sopenharmony_ci */
598c2ecf20Sopenharmony_cistatic __always_inline void tlbiel_hash_set_isa300(unsigned int set, unsigned int is,
608c2ecf20Sopenharmony_ci					unsigned int pid,
618c2ecf20Sopenharmony_ci					unsigned int ric, unsigned int prs)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	unsigned long rb;
648c2ecf20Sopenharmony_ci	unsigned long rs;
658c2ecf20Sopenharmony_ci	unsigned int r = 0; /* hash format */
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	rb = (set << PPC_BITLSHIFT(51)) | (is << PPC_BITLSHIFT(53));
688c2ecf20Sopenharmony_ci	rs = ((unsigned long)pid << PPC_BITLSHIFT(31));
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	asm volatile(PPC_TLBIEL(%0, %1, %2, %3, %4)
718c2ecf20Sopenharmony_ci		     : : "r"(rb), "r"(rs), "i"(ric), "i"(prs), "i"(r)
728c2ecf20Sopenharmony_ci		     : "memory");
738c2ecf20Sopenharmony_ci}
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_cistatic void tlbiel_all_isa206(unsigned int num_sets, unsigned int is)
778c2ecf20Sopenharmony_ci{
788c2ecf20Sopenharmony_ci	unsigned int set;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	asm volatile("ptesync": : :"memory");
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	for (set = 0; set < num_sets; set++)
838c2ecf20Sopenharmony_ci		tlbiel_hash_set_isa206(set, is);
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	ppc_after_tlbiel_barrier();
868c2ecf20Sopenharmony_ci}
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_cistatic void tlbiel_all_isa300(unsigned int num_sets, unsigned int is)
898c2ecf20Sopenharmony_ci{
908c2ecf20Sopenharmony_ci	unsigned int set;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	asm volatile("ptesync": : :"memory");
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	/*
958c2ecf20Sopenharmony_ci	 * Flush the partition table cache if this is HV mode.
968c2ecf20Sopenharmony_ci	 */
978c2ecf20Sopenharmony_ci	if (early_cpu_has_feature(CPU_FTR_HVMODE))
988c2ecf20Sopenharmony_ci		tlbiel_hash_set_isa300(0, is, 0, 2, 0);
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	/*
1018c2ecf20Sopenharmony_ci	 * Now invalidate the process table cache. UPRT=0 HPT modes (what
1028c2ecf20Sopenharmony_ci	 * current hardware implements) do not use the process table, but
1038c2ecf20Sopenharmony_ci	 * add the flushes anyway.
1048c2ecf20Sopenharmony_ci	 *
1058c2ecf20Sopenharmony_ci	 * From ISA v3.0B p. 1078:
1068c2ecf20Sopenharmony_ci	 *     The following forms are invalid.
1078c2ecf20Sopenharmony_ci	 *      * PRS=1, R=0, and RIC!=2 (The only process-scoped
1088c2ecf20Sopenharmony_ci	 *        HPT caching is of the Process Table.)
1098c2ecf20Sopenharmony_ci	 */
1108c2ecf20Sopenharmony_ci	tlbiel_hash_set_isa300(0, is, 0, 2, 1);
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	/*
1138c2ecf20Sopenharmony_ci	 * Then flush the sets of the TLB proper. Hash mode uses
1148c2ecf20Sopenharmony_ci	 * partition scoped TLB translations, which may be flushed
1158c2ecf20Sopenharmony_ci	 * in !HV mode.
1168c2ecf20Sopenharmony_ci	 */
1178c2ecf20Sopenharmony_ci	for (set = 0; set < num_sets; set++)
1188c2ecf20Sopenharmony_ci		tlbiel_hash_set_isa300(set, is, 0, 0, 0);
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	ppc_after_tlbiel_barrier();
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	asm volatile(PPC_ISA_3_0_INVALIDATE_ERAT "; isync" : : :"memory");
1238c2ecf20Sopenharmony_ci}
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_civoid hash__tlbiel_all(unsigned int action)
1268c2ecf20Sopenharmony_ci{
1278c2ecf20Sopenharmony_ci	unsigned int is;
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	switch (action) {
1308c2ecf20Sopenharmony_ci	case TLB_INVAL_SCOPE_GLOBAL:
1318c2ecf20Sopenharmony_ci		is = 3;
1328c2ecf20Sopenharmony_ci		break;
1338c2ecf20Sopenharmony_ci	case TLB_INVAL_SCOPE_LPID:
1348c2ecf20Sopenharmony_ci		is = 2;
1358c2ecf20Sopenharmony_ci		break;
1368c2ecf20Sopenharmony_ci	default:
1378c2ecf20Sopenharmony_ci		BUG();
1388c2ecf20Sopenharmony_ci	}
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	if (early_cpu_has_feature(CPU_FTR_ARCH_300))
1418c2ecf20Sopenharmony_ci		tlbiel_all_isa300(POWER9_TLB_SETS_HASH, is);
1428c2ecf20Sopenharmony_ci	else if (early_cpu_has_feature(CPU_FTR_ARCH_207S))
1438c2ecf20Sopenharmony_ci		tlbiel_all_isa206(POWER8_TLB_SETS, is);
1448c2ecf20Sopenharmony_ci	else if (early_cpu_has_feature(CPU_FTR_ARCH_206))
1458c2ecf20Sopenharmony_ci		tlbiel_all_isa206(POWER7_TLB_SETS, is);
1468c2ecf20Sopenharmony_ci	else
1478c2ecf20Sopenharmony_ci		WARN(1, "%s called on pre-POWER7 CPU\n", __func__);
1488c2ecf20Sopenharmony_ci}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_cistatic inline unsigned long  ___tlbie(unsigned long vpn, int psize,
1518c2ecf20Sopenharmony_ci						int apsize, int ssize)
1528c2ecf20Sopenharmony_ci{
1538c2ecf20Sopenharmony_ci	unsigned long va;
1548c2ecf20Sopenharmony_ci	unsigned int penc;
1558c2ecf20Sopenharmony_ci	unsigned long sllp;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	/*
1588c2ecf20Sopenharmony_ci	 * We need 14 to 65 bits of va for a tlibe of 4K page
1598c2ecf20Sopenharmony_ci	 * With vpn we ignore the lower VPN_SHIFT bits already.
1608c2ecf20Sopenharmony_ci	 * And top two bits are already ignored because we can
1618c2ecf20Sopenharmony_ci	 * only accomodate 76 bits in a 64 bit vpn with a VPN_SHIFT
1628c2ecf20Sopenharmony_ci	 * of 12.
1638c2ecf20Sopenharmony_ci	 */
1648c2ecf20Sopenharmony_ci	va = vpn << VPN_SHIFT;
1658c2ecf20Sopenharmony_ci	/*
1668c2ecf20Sopenharmony_ci	 * clear top 16 bits of 64bit va, non SLS segment
1678c2ecf20Sopenharmony_ci	 * Older versions of the architecture (2.02 and earler) require the
1688c2ecf20Sopenharmony_ci	 * masking of the top 16 bits.
1698c2ecf20Sopenharmony_ci	 */
1708c2ecf20Sopenharmony_ci	if (mmu_has_feature(MMU_FTR_TLBIE_CROP_VA))
1718c2ecf20Sopenharmony_ci		va &= ~(0xffffULL << 48);
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	switch (psize) {
1748c2ecf20Sopenharmony_ci	case MMU_PAGE_4K:
1758c2ecf20Sopenharmony_ci		/* clear out bits after (52) [0....52.....63] */
1768c2ecf20Sopenharmony_ci		va &= ~((1ul << (64 - 52)) - 1);
1778c2ecf20Sopenharmony_ci		va |= ssize << 8;
1788c2ecf20Sopenharmony_ci		sllp = get_sllp_encoding(apsize);
1798c2ecf20Sopenharmony_ci		va |= sllp << 5;
1808c2ecf20Sopenharmony_ci		asm volatile(ASM_FTR_IFCLR("tlbie %0,0", PPC_TLBIE(%1,%0), %2)
1818c2ecf20Sopenharmony_ci			     : : "r" (va), "r"(0), "i" (CPU_FTR_ARCH_206)
1828c2ecf20Sopenharmony_ci			     : "memory");
1838c2ecf20Sopenharmony_ci		break;
1848c2ecf20Sopenharmony_ci	default:
1858c2ecf20Sopenharmony_ci		/* We need 14 to 14 + i bits of va */
1868c2ecf20Sopenharmony_ci		penc = mmu_psize_defs[psize].penc[apsize];
1878c2ecf20Sopenharmony_ci		va &= ~((1ul << mmu_psize_defs[apsize].shift) - 1);
1888c2ecf20Sopenharmony_ci		va |= penc << 12;
1898c2ecf20Sopenharmony_ci		va |= ssize << 8;
1908c2ecf20Sopenharmony_ci		/*
1918c2ecf20Sopenharmony_ci		 * AVAL bits:
1928c2ecf20Sopenharmony_ci		 * We don't need all the bits, but rest of the bits
1938c2ecf20Sopenharmony_ci		 * must be ignored by the processor.
1948c2ecf20Sopenharmony_ci		 * vpn cover upto 65 bits of va. (0...65) and we need
1958c2ecf20Sopenharmony_ci		 * 58..64 bits of va.
1968c2ecf20Sopenharmony_ci		 */
1978c2ecf20Sopenharmony_ci		va |= (vpn & 0xfe); /* AVAL */
1988c2ecf20Sopenharmony_ci		va |= 1; /* L */
1998c2ecf20Sopenharmony_ci		asm volatile(ASM_FTR_IFCLR("tlbie %0,1", PPC_TLBIE(%1,%0), %2)
2008c2ecf20Sopenharmony_ci			     : : "r" (va), "r"(0), "i" (CPU_FTR_ARCH_206)
2018c2ecf20Sopenharmony_ci			     : "memory");
2028c2ecf20Sopenharmony_ci		break;
2038c2ecf20Sopenharmony_ci	}
2048c2ecf20Sopenharmony_ci	return va;
2058c2ecf20Sopenharmony_ci}
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_cistatic inline void fixup_tlbie_vpn(unsigned long vpn, int psize,
2088c2ecf20Sopenharmony_ci				   int apsize, int ssize)
2098c2ecf20Sopenharmony_ci{
2108c2ecf20Sopenharmony_ci	if (cpu_has_feature(CPU_FTR_P9_TLBIE_ERAT_BUG)) {
2118c2ecf20Sopenharmony_ci		/* Radix flush for a hash guest */
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci		unsigned long rb,rs,prs,r,ric;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci		rb = PPC_BIT(52); /* IS = 2 */
2168c2ecf20Sopenharmony_ci		rs = 0;  /* lpid = 0 */
2178c2ecf20Sopenharmony_ci		prs = 0; /* partition scoped */
2188c2ecf20Sopenharmony_ci		r = 1;   /* radix format */
2198c2ecf20Sopenharmony_ci		ric = 0; /* RIC_FLSUH_TLB */
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci		/*
2228c2ecf20Sopenharmony_ci		 * Need the extra ptesync to make sure we don't
2238c2ecf20Sopenharmony_ci		 * re-order the tlbie
2248c2ecf20Sopenharmony_ci		 */
2258c2ecf20Sopenharmony_ci		asm volatile("ptesync": : :"memory");
2268c2ecf20Sopenharmony_ci		asm volatile(PPC_TLBIE_5(%0, %4, %3, %2, %1)
2278c2ecf20Sopenharmony_ci			     : : "r"(rb), "i"(r), "i"(prs),
2288c2ecf20Sopenharmony_ci			       "i"(ric), "r"(rs) : "memory");
2298c2ecf20Sopenharmony_ci	}
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	if (cpu_has_feature(CPU_FTR_P9_TLBIE_STQ_BUG)) {
2338c2ecf20Sopenharmony_ci		/* Need the extra ptesync to ensure we don't reorder tlbie*/
2348c2ecf20Sopenharmony_ci		asm volatile("ptesync": : :"memory");
2358c2ecf20Sopenharmony_ci		___tlbie(vpn, psize, apsize, ssize);
2368c2ecf20Sopenharmony_ci	}
2378c2ecf20Sopenharmony_ci}
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_cistatic inline void __tlbie(unsigned long vpn, int psize, int apsize, int ssize)
2408c2ecf20Sopenharmony_ci{
2418c2ecf20Sopenharmony_ci	unsigned long rb;
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	rb = ___tlbie(vpn, psize, apsize, ssize);
2448c2ecf20Sopenharmony_ci	trace_tlbie(0, 0, rb, 0, 0, 0, 0);
2458c2ecf20Sopenharmony_ci}
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_cistatic inline void __tlbiel(unsigned long vpn, int psize, int apsize, int ssize)
2488c2ecf20Sopenharmony_ci{
2498c2ecf20Sopenharmony_ci	unsigned long va;
2508c2ecf20Sopenharmony_ci	unsigned int penc;
2518c2ecf20Sopenharmony_ci	unsigned long sllp;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	/* VPN_SHIFT can be atmost 12 */
2548c2ecf20Sopenharmony_ci	va = vpn << VPN_SHIFT;
2558c2ecf20Sopenharmony_ci	/*
2568c2ecf20Sopenharmony_ci	 * clear top 16 bits of 64 bit va, non SLS segment
2578c2ecf20Sopenharmony_ci	 * Older versions of the architecture (2.02 and earler) require the
2588c2ecf20Sopenharmony_ci	 * masking of the top 16 bits.
2598c2ecf20Sopenharmony_ci	 */
2608c2ecf20Sopenharmony_ci	if (mmu_has_feature(MMU_FTR_TLBIE_CROP_VA))
2618c2ecf20Sopenharmony_ci		va &= ~(0xffffULL << 48);
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	switch (psize) {
2648c2ecf20Sopenharmony_ci	case MMU_PAGE_4K:
2658c2ecf20Sopenharmony_ci		/* clear out bits after(52) [0....52.....63] */
2668c2ecf20Sopenharmony_ci		va &= ~((1ul << (64 - 52)) - 1);
2678c2ecf20Sopenharmony_ci		va |= ssize << 8;
2688c2ecf20Sopenharmony_ci		sllp = get_sllp_encoding(apsize);
2698c2ecf20Sopenharmony_ci		va |= sllp << 5;
2708c2ecf20Sopenharmony_ci		asm volatile(ASM_FTR_IFSET("tlbiel %0", "tlbiel %0,0", %1)
2718c2ecf20Sopenharmony_ci			     : : "r" (va), "i" (CPU_FTR_ARCH_206)
2728c2ecf20Sopenharmony_ci			     : "memory");
2738c2ecf20Sopenharmony_ci		break;
2748c2ecf20Sopenharmony_ci	default:
2758c2ecf20Sopenharmony_ci		/* We need 14 to 14 + i bits of va */
2768c2ecf20Sopenharmony_ci		penc = mmu_psize_defs[psize].penc[apsize];
2778c2ecf20Sopenharmony_ci		va &= ~((1ul << mmu_psize_defs[apsize].shift) - 1);
2788c2ecf20Sopenharmony_ci		va |= penc << 12;
2798c2ecf20Sopenharmony_ci		va |= ssize << 8;
2808c2ecf20Sopenharmony_ci		/*
2818c2ecf20Sopenharmony_ci		 * AVAL bits:
2828c2ecf20Sopenharmony_ci		 * We don't need all the bits, but rest of the bits
2838c2ecf20Sopenharmony_ci		 * must be ignored by the processor.
2848c2ecf20Sopenharmony_ci		 * vpn cover upto 65 bits of va. (0...65) and we need
2858c2ecf20Sopenharmony_ci		 * 58..64 bits of va.
2868c2ecf20Sopenharmony_ci		 */
2878c2ecf20Sopenharmony_ci		va |= (vpn & 0xfe);
2888c2ecf20Sopenharmony_ci		va |= 1; /* L */
2898c2ecf20Sopenharmony_ci		asm volatile(ASM_FTR_IFSET("tlbiel %0", "tlbiel %0,1", %1)
2908c2ecf20Sopenharmony_ci			     : : "r" (va), "i" (CPU_FTR_ARCH_206)
2918c2ecf20Sopenharmony_ci			     : "memory");
2928c2ecf20Sopenharmony_ci		break;
2938c2ecf20Sopenharmony_ci	}
2948c2ecf20Sopenharmony_ci	trace_tlbie(0, 1, va, 0, 0, 0, 0);
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci}
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_cistatic inline void tlbie(unsigned long vpn, int psize, int apsize,
2998c2ecf20Sopenharmony_ci			 int ssize, int local)
3008c2ecf20Sopenharmony_ci{
3018c2ecf20Sopenharmony_ci	unsigned int use_local;
3028c2ecf20Sopenharmony_ci	int lock_tlbie = !mmu_has_feature(MMU_FTR_LOCKLESS_TLBIE);
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	use_local = local && mmu_has_feature(MMU_FTR_TLBIEL) && !cxl_ctx_in_use();
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	if (use_local)
3078c2ecf20Sopenharmony_ci		use_local = mmu_psize_defs[psize].tlbiel;
3088c2ecf20Sopenharmony_ci	if (lock_tlbie && !use_local)
3098c2ecf20Sopenharmony_ci		raw_spin_lock(&native_tlbie_lock);
3108c2ecf20Sopenharmony_ci	asm volatile("ptesync": : :"memory");
3118c2ecf20Sopenharmony_ci	if (use_local) {
3128c2ecf20Sopenharmony_ci		__tlbiel(vpn, psize, apsize, ssize);
3138c2ecf20Sopenharmony_ci		ppc_after_tlbiel_barrier();
3148c2ecf20Sopenharmony_ci	} else {
3158c2ecf20Sopenharmony_ci		__tlbie(vpn, psize, apsize, ssize);
3168c2ecf20Sopenharmony_ci		fixup_tlbie_vpn(vpn, psize, apsize, ssize);
3178c2ecf20Sopenharmony_ci		asm volatile("eieio; tlbsync; ptesync": : :"memory");
3188c2ecf20Sopenharmony_ci	}
3198c2ecf20Sopenharmony_ci	if (lock_tlbie && !use_local)
3208c2ecf20Sopenharmony_ci		raw_spin_unlock(&native_tlbie_lock);
3218c2ecf20Sopenharmony_ci}
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_cistatic inline void native_lock_hpte(struct hash_pte *hptep)
3248c2ecf20Sopenharmony_ci{
3258c2ecf20Sopenharmony_ci	unsigned long *word = (unsigned long *)&hptep->v;
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	while (1) {
3288c2ecf20Sopenharmony_ci		if (!test_and_set_bit_lock(HPTE_LOCK_BIT, word))
3298c2ecf20Sopenharmony_ci			break;
3308c2ecf20Sopenharmony_ci		spin_begin();
3318c2ecf20Sopenharmony_ci		while(test_bit(HPTE_LOCK_BIT, word))
3328c2ecf20Sopenharmony_ci			spin_cpu_relax();
3338c2ecf20Sopenharmony_ci		spin_end();
3348c2ecf20Sopenharmony_ci	}
3358c2ecf20Sopenharmony_ci}
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_cistatic inline void native_unlock_hpte(struct hash_pte *hptep)
3388c2ecf20Sopenharmony_ci{
3398c2ecf20Sopenharmony_ci	unsigned long *word = (unsigned long *)&hptep->v;
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	clear_bit_unlock(HPTE_LOCK_BIT, word);
3428c2ecf20Sopenharmony_ci}
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_cistatic long native_hpte_insert(unsigned long hpte_group, unsigned long vpn,
3458c2ecf20Sopenharmony_ci			unsigned long pa, unsigned long rflags,
3468c2ecf20Sopenharmony_ci			unsigned long vflags, int psize, int apsize, int ssize)
3478c2ecf20Sopenharmony_ci{
3488c2ecf20Sopenharmony_ci	struct hash_pte *hptep = htab_address + hpte_group;
3498c2ecf20Sopenharmony_ci	unsigned long hpte_v, hpte_r;
3508c2ecf20Sopenharmony_ci	int i;
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	if (!(vflags & HPTE_V_BOLTED)) {
3538c2ecf20Sopenharmony_ci		DBG_LOW("    insert(group=%lx, vpn=%016lx, pa=%016lx,"
3548c2ecf20Sopenharmony_ci			" rflags=%lx, vflags=%lx, psize=%d)\n",
3558c2ecf20Sopenharmony_ci			hpte_group, vpn, pa, rflags, vflags, psize);
3568c2ecf20Sopenharmony_ci	}
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	for (i = 0; i < HPTES_PER_GROUP; i++) {
3598c2ecf20Sopenharmony_ci		if (! (be64_to_cpu(hptep->v) & HPTE_V_VALID)) {
3608c2ecf20Sopenharmony_ci			/* retry with lock held */
3618c2ecf20Sopenharmony_ci			native_lock_hpte(hptep);
3628c2ecf20Sopenharmony_ci			if (! (be64_to_cpu(hptep->v) & HPTE_V_VALID))
3638c2ecf20Sopenharmony_ci				break;
3648c2ecf20Sopenharmony_ci			native_unlock_hpte(hptep);
3658c2ecf20Sopenharmony_ci		}
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci		hptep++;
3688c2ecf20Sopenharmony_ci	}
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	if (i == HPTES_PER_GROUP)
3718c2ecf20Sopenharmony_ci		return -1;
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	hpte_v = hpte_encode_v(vpn, psize, apsize, ssize) | vflags | HPTE_V_VALID;
3748c2ecf20Sopenharmony_ci	hpte_r = hpte_encode_r(pa, psize, apsize) | rflags;
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	if (!(vflags & HPTE_V_BOLTED)) {
3778c2ecf20Sopenharmony_ci		DBG_LOW(" i=%x hpte_v=%016lx, hpte_r=%016lx\n",
3788c2ecf20Sopenharmony_ci			i, hpte_v, hpte_r);
3798c2ecf20Sopenharmony_ci	}
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	if (cpu_has_feature(CPU_FTR_ARCH_300)) {
3828c2ecf20Sopenharmony_ci		hpte_r = hpte_old_to_new_r(hpte_v, hpte_r);
3838c2ecf20Sopenharmony_ci		hpte_v = hpte_old_to_new_v(hpte_v);
3848c2ecf20Sopenharmony_ci	}
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	hptep->r = cpu_to_be64(hpte_r);
3878c2ecf20Sopenharmony_ci	/* Guarantee the second dword is visible before the valid bit */
3888c2ecf20Sopenharmony_ci	eieio();
3898c2ecf20Sopenharmony_ci	/*
3908c2ecf20Sopenharmony_ci	 * Now set the first dword including the valid bit
3918c2ecf20Sopenharmony_ci	 * NOTE: this also unlocks the hpte
3928c2ecf20Sopenharmony_ci	 */
3938c2ecf20Sopenharmony_ci	hptep->v = cpu_to_be64(hpte_v);
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	__asm__ __volatile__ ("ptesync" : : : "memory");
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	return i | (!!(vflags & HPTE_V_SECONDARY) << 3);
3988c2ecf20Sopenharmony_ci}
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_cistatic long native_hpte_remove(unsigned long hpte_group)
4018c2ecf20Sopenharmony_ci{
4028c2ecf20Sopenharmony_ci	struct hash_pte *hptep;
4038c2ecf20Sopenharmony_ci	int i;
4048c2ecf20Sopenharmony_ci	int slot_offset;
4058c2ecf20Sopenharmony_ci	unsigned long hpte_v;
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci	DBG_LOW("    remove(group=%lx)\n", hpte_group);
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	/* pick a random entry to start at */
4108c2ecf20Sopenharmony_ci	slot_offset = mftb() & 0x7;
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci	for (i = 0; i < HPTES_PER_GROUP; i++) {
4138c2ecf20Sopenharmony_ci		hptep = htab_address + hpte_group + slot_offset;
4148c2ecf20Sopenharmony_ci		hpte_v = be64_to_cpu(hptep->v);
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci		if ((hpte_v & HPTE_V_VALID) && !(hpte_v & HPTE_V_BOLTED)) {
4178c2ecf20Sopenharmony_ci			/* retry with lock held */
4188c2ecf20Sopenharmony_ci			native_lock_hpte(hptep);
4198c2ecf20Sopenharmony_ci			hpte_v = be64_to_cpu(hptep->v);
4208c2ecf20Sopenharmony_ci			if ((hpte_v & HPTE_V_VALID)
4218c2ecf20Sopenharmony_ci			    && !(hpte_v & HPTE_V_BOLTED))
4228c2ecf20Sopenharmony_ci				break;
4238c2ecf20Sopenharmony_ci			native_unlock_hpte(hptep);
4248c2ecf20Sopenharmony_ci		}
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci		slot_offset++;
4278c2ecf20Sopenharmony_ci		slot_offset &= 0x7;
4288c2ecf20Sopenharmony_ci	}
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci	if (i == HPTES_PER_GROUP)
4318c2ecf20Sopenharmony_ci		return -1;
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci	/* Invalidate the hpte. NOTE: this also unlocks it */
4348c2ecf20Sopenharmony_ci	hptep->v = 0;
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci	return i;
4378c2ecf20Sopenharmony_ci}
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_cistatic long native_hpte_updatepp(unsigned long slot, unsigned long newpp,
4408c2ecf20Sopenharmony_ci				 unsigned long vpn, int bpsize,
4418c2ecf20Sopenharmony_ci				 int apsize, int ssize, unsigned long flags)
4428c2ecf20Sopenharmony_ci{
4438c2ecf20Sopenharmony_ci	struct hash_pte *hptep = htab_address + slot;
4448c2ecf20Sopenharmony_ci	unsigned long hpte_v, want_v;
4458c2ecf20Sopenharmony_ci	int ret = 0, local = 0;
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci	want_v = hpte_encode_avpn(vpn, bpsize, ssize);
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	DBG_LOW("    update(vpn=%016lx, avpnv=%016lx, group=%lx, newpp=%lx)",
4508c2ecf20Sopenharmony_ci		vpn, want_v & HPTE_V_AVPN, slot, newpp);
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci	hpte_v = hpte_get_old_v(hptep);
4538c2ecf20Sopenharmony_ci	/*
4548c2ecf20Sopenharmony_ci	 * We need to invalidate the TLB always because hpte_remove doesn't do
4558c2ecf20Sopenharmony_ci	 * a tlb invalidate. If a hash bucket gets full, we "evict" a more/less
4568c2ecf20Sopenharmony_ci	 * random entry from it. When we do that we don't invalidate the TLB
4578c2ecf20Sopenharmony_ci	 * (hpte_remove) because we assume the old translation is still
4588c2ecf20Sopenharmony_ci	 * technically "valid".
4598c2ecf20Sopenharmony_ci	 */
4608c2ecf20Sopenharmony_ci	if (!HPTE_V_COMPARE(hpte_v, want_v) || !(hpte_v & HPTE_V_VALID)) {
4618c2ecf20Sopenharmony_ci		DBG_LOW(" -> miss\n");
4628c2ecf20Sopenharmony_ci		ret = -1;
4638c2ecf20Sopenharmony_ci	} else {
4648c2ecf20Sopenharmony_ci		native_lock_hpte(hptep);
4658c2ecf20Sopenharmony_ci		/* recheck with locks held */
4668c2ecf20Sopenharmony_ci		hpte_v = hpte_get_old_v(hptep);
4678c2ecf20Sopenharmony_ci		if (unlikely(!HPTE_V_COMPARE(hpte_v, want_v) ||
4688c2ecf20Sopenharmony_ci			     !(hpte_v & HPTE_V_VALID))) {
4698c2ecf20Sopenharmony_ci			ret = -1;
4708c2ecf20Sopenharmony_ci		} else {
4718c2ecf20Sopenharmony_ci			DBG_LOW(" -> hit\n");
4728c2ecf20Sopenharmony_ci			/* Update the HPTE */
4738c2ecf20Sopenharmony_ci			hptep->r = cpu_to_be64((be64_to_cpu(hptep->r) &
4748c2ecf20Sopenharmony_ci						~(HPTE_R_PPP | HPTE_R_N)) |
4758c2ecf20Sopenharmony_ci					       (newpp & (HPTE_R_PPP | HPTE_R_N |
4768c2ecf20Sopenharmony_ci							 HPTE_R_C)));
4778c2ecf20Sopenharmony_ci		}
4788c2ecf20Sopenharmony_ci		native_unlock_hpte(hptep);
4798c2ecf20Sopenharmony_ci	}
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci	if (flags & HPTE_LOCAL_UPDATE)
4828c2ecf20Sopenharmony_ci		local = 1;
4838c2ecf20Sopenharmony_ci	/*
4848c2ecf20Sopenharmony_ci	 * Ensure it is out of the tlb too if it is not a nohpte fault
4858c2ecf20Sopenharmony_ci	 */
4868c2ecf20Sopenharmony_ci	if (!(flags & HPTE_NOHPTE_UPDATE))
4878c2ecf20Sopenharmony_ci		tlbie(vpn, bpsize, apsize, ssize, local);
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci	return ret;
4908c2ecf20Sopenharmony_ci}
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_cistatic long __native_hpte_find(unsigned long want_v, unsigned long slot)
4938c2ecf20Sopenharmony_ci{
4948c2ecf20Sopenharmony_ci	struct hash_pte *hptep;
4958c2ecf20Sopenharmony_ci	unsigned long hpte_v;
4968c2ecf20Sopenharmony_ci	unsigned long i;
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ci	for (i = 0; i < HPTES_PER_GROUP; i++) {
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ci		hptep = htab_address + slot;
5018c2ecf20Sopenharmony_ci		hpte_v = hpte_get_old_v(hptep);
5028c2ecf20Sopenharmony_ci		if (HPTE_V_COMPARE(hpte_v, want_v) && (hpte_v & HPTE_V_VALID))
5038c2ecf20Sopenharmony_ci			/* HPTE matches */
5048c2ecf20Sopenharmony_ci			return slot;
5058c2ecf20Sopenharmony_ci		++slot;
5068c2ecf20Sopenharmony_ci	}
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci	return -1;
5098c2ecf20Sopenharmony_ci}
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_cistatic long native_hpte_find(unsigned long vpn, int psize, int ssize)
5128c2ecf20Sopenharmony_ci{
5138c2ecf20Sopenharmony_ci	unsigned long hpte_group;
5148c2ecf20Sopenharmony_ci	unsigned long want_v;
5158c2ecf20Sopenharmony_ci	unsigned long hash;
5168c2ecf20Sopenharmony_ci	long slot;
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci	hash = hpt_hash(vpn, mmu_psize_defs[psize].shift, ssize);
5198c2ecf20Sopenharmony_ci	want_v = hpte_encode_avpn(vpn, psize, ssize);
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci	/*
5228c2ecf20Sopenharmony_ci	 * We try to keep bolted entries always in primary hash
5238c2ecf20Sopenharmony_ci	 * But in some case we can find them in secondary too.
5248c2ecf20Sopenharmony_ci	 */
5258c2ecf20Sopenharmony_ci	hpte_group = (hash & htab_hash_mask) * HPTES_PER_GROUP;
5268c2ecf20Sopenharmony_ci	slot = __native_hpte_find(want_v, hpte_group);
5278c2ecf20Sopenharmony_ci	if (slot < 0) {
5288c2ecf20Sopenharmony_ci		/* Try in secondary */
5298c2ecf20Sopenharmony_ci		hpte_group = (~hash & htab_hash_mask) * HPTES_PER_GROUP;
5308c2ecf20Sopenharmony_ci		slot = __native_hpte_find(want_v, hpte_group);
5318c2ecf20Sopenharmony_ci		if (slot < 0)
5328c2ecf20Sopenharmony_ci			return -1;
5338c2ecf20Sopenharmony_ci	}
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci	return slot;
5368c2ecf20Sopenharmony_ci}
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_ci/*
5398c2ecf20Sopenharmony_ci * Update the page protection bits. Intended to be used to create
5408c2ecf20Sopenharmony_ci * guard pages for kernel data structures on pages which are bolted
5418c2ecf20Sopenharmony_ci * in the HPT. Assumes pages being operated on will not be stolen.
5428c2ecf20Sopenharmony_ci *
5438c2ecf20Sopenharmony_ci * No need to lock here because we should be the only user.
5448c2ecf20Sopenharmony_ci */
5458c2ecf20Sopenharmony_cistatic void native_hpte_updateboltedpp(unsigned long newpp, unsigned long ea,
5468c2ecf20Sopenharmony_ci				       int psize, int ssize)
5478c2ecf20Sopenharmony_ci{
5488c2ecf20Sopenharmony_ci	unsigned long vpn;
5498c2ecf20Sopenharmony_ci	unsigned long vsid;
5508c2ecf20Sopenharmony_ci	long slot;
5518c2ecf20Sopenharmony_ci	struct hash_pte *hptep;
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ci	vsid = get_kernel_vsid(ea, ssize);
5548c2ecf20Sopenharmony_ci	vpn = hpt_vpn(ea, vsid, ssize);
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci	slot = native_hpte_find(vpn, psize, ssize);
5578c2ecf20Sopenharmony_ci	if (slot == -1)
5588c2ecf20Sopenharmony_ci		panic("could not find page to bolt\n");
5598c2ecf20Sopenharmony_ci	hptep = htab_address + slot;
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci	/* Update the HPTE */
5628c2ecf20Sopenharmony_ci	hptep->r = cpu_to_be64((be64_to_cpu(hptep->r) &
5638c2ecf20Sopenharmony_ci				~(HPTE_R_PPP | HPTE_R_N)) |
5648c2ecf20Sopenharmony_ci			       (newpp & (HPTE_R_PPP | HPTE_R_N)));
5658c2ecf20Sopenharmony_ci	/*
5668c2ecf20Sopenharmony_ci	 * Ensure it is out of the tlb too. Bolted entries base and
5678c2ecf20Sopenharmony_ci	 * actual page size will be same.
5688c2ecf20Sopenharmony_ci	 */
5698c2ecf20Sopenharmony_ci	tlbie(vpn, psize, psize, ssize, 0);
5708c2ecf20Sopenharmony_ci}
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci/*
5738c2ecf20Sopenharmony_ci * Remove a bolted kernel entry. Memory hotplug uses this.
5748c2ecf20Sopenharmony_ci *
5758c2ecf20Sopenharmony_ci * No need to lock here because we should be the only user.
5768c2ecf20Sopenharmony_ci */
5778c2ecf20Sopenharmony_cistatic int native_hpte_removebolted(unsigned long ea, int psize, int ssize)
5788c2ecf20Sopenharmony_ci{
5798c2ecf20Sopenharmony_ci	unsigned long vpn;
5808c2ecf20Sopenharmony_ci	unsigned long vsid;
5818c2ecf20Sopenharmony_ci	long slot;
5828c2ecf20Sopenharmony_ci	struct hash_pte *hptep;
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci	vsid = get_kernel_vsid(ea, ssize);
5858c2ecf20Sopenharmony_ci	vpn = hpt_vpn(ea, vsid, ssize);
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci	slot = native_hpte_find(vpn, psize, ssize);
5888c2ecf20Sopenharmony_ci	if (slot == -1)
5898c2ecf20Sopenharmony_ci		return -ENOENT;
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci	hptep = htab_address + slot;
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci	VM_WARN_ON(!(be64_to_cpu(hptep->v) & HPTE_V_BOLTED));
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_ci	/* Invalidate the hpte */
5968c2ecf20Sopenharmony_ci	hptep->v = 0;
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_ci	/* Invalidate the TLB */
5998c2ecf20Sopenharmony_ci	tlbie(vpn, psize, psize, ssize, 0);
6008c2ecf20Sopenharmony_ci	return 0;
6018c2ecf20Sopenharmony_ci}
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_cistatic void native_hpte_invalidate(unsigned long slot, unsigned long vpn,
6058c2ecf20Sopenharmony_ci				   int bpsize, int apsize, int ssize, int local)
6068c2ecf20Sopenharmony_ci{
6078c2ecf20Sopenharmony_ci	struct hash_pte *hptep = htab_address + slot;
6088c2ecf20Sopenharmony_ci	unsigned long hpte_v;
6098c2ecf20Sopenharmony_ci	unsigned long want_v;
6108c2ecf20Sopenharmony_ci	unsigned long flags;
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_ci	local_irq_save(flags);
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ci	DBG_LOW("    invalidate(vpn=%016lx, hash: %lx)\n", vpn, slot);
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_ci	want_v = hpte_encode_avpn(vpn, bpsize, ssize);
6178c2ecf20Sopenharmony_ci	hpte_v = hpte_get_old_v(hptep);
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci	if (HPTE_V_COMPARE(hpte_v, want_v) && (hpte_v & HPTE_V_VALID)) {
6208c2ecf20Sopenharmony_ci		native_lock_hpte(hptep);
6218c2ecf20Sopenharmony_ci		/* recheck with locks held */
6228c2ecf20Sopenharmony_ci		hpte_v = hpte_get_old_v(hptep);
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci		if (HPTE_V_COMPARE(hpte_v, want_v) && (hpte_v & HPTE_V_VALID))
6258c2ecf20Sopenharmony_ci			/* Invalidate the hpte. NOTE: this also unlocks it */
6268c2ecf20Sopenharmony_ci			hptep->v = 0;
6278c2ecf20Sopenharmony_ci		else
6288c2ecf20Sopenharmony_ci			native_unlock_hpte(hptep);
6298c2ecf20Sopenharmony_ci	}
6308c2ecf20Sopenharmony_ci	/*
6318c2ecf20Sopenharmony_ci	 * We need to invalidate the TLB always because hpte_remove doesn't do
6328c2ecf20Sopenharmony_ci	 * a tlb invalidate. If a hash bucket gets full, we "evict" a more/less
6338c2ecf20Sopenharmony_ci	 * random entry from it. When we do that we don't invalidate the TLB
6348c2ecf20Sopenharmony_ci	 * (hpte_remove) because we assume the old translation is still
6358c2ecf20Sopenharmony_ci	 * technically "valid".
6368c2ecf20Sopenharmony_ci	 */
6378c2ecf20Sopenharmony_ci	tlbie(vpn, bpsize, apsize, ssize, local);
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci	local_irq_restore(flags);
6408c2ecf20Sopenharmony_ci}
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_ci#ifdef CONFIG_TRANSPARENT_HUGEPAGE
6438c2ecf20Sopenharmony_cistatic void native_hugepage_invalidate(unsigned long vsid,
6448c2ecf20Sopenharmony_ci				       unsigned long addr,
6458c2ecf20Sopenharmony_ci				       unsigned char *hpte_slot_array,
6468c2ecf20Sopenharmony_ci				       int psize, int ssize, int local)
6478c2ecf20Sopenharmony_ci{
6488c2ecf20Sopenharmony_ci	int i;
6498c2ecf20Sopenharmony_ci	struct hash_pte *hptep;
6508c2ecf20Sopenharmony_ci	int actual_psize = MMU_PAGE_16M;
6518c2ecf20Sopenharmony_ci	unsigned int max_hpte_count, valid;
6528c2ecf20Sopenharmony_ci	unsigned long flags, s_addr = addr;
6538c2ecf20Sopenharmony_ci	unsigned long hpte_v, want_v, shift;
6548c2ecf20Sopenharmony_ci	unsigned long hidx, vpn = 0, hash, slot;
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci	shift = mmu_psize_defs[psize].shift;
6578c2ecf20Sopenharmony_ci	max_hpte_count = 1U << (PMD_SHIFT - shift);
6588c2ecf20Sopenharmony_ci
6598c2ecf20Sopenharmony_ci	local_irq_save(flags);
6608c2ecf20Sopenharmony_ci	for (i = 0; i < max_hpte_count; i++) {
6618c2ecf20Sopenharmony_ci		valid = hpte_valid(hpte_slot_array, i);
6628c2ecf20Sopenharmony_ci		if (!valid)
6638c2ecf20Sopenharmony_ci			continue;
6648c2ecf20Sopenharmony_ci		hidx =  hpte_hash_index(hpte_slot_array, i);
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ci		/* get the vpn */
6678c2ecf20Sopenharmony_ci		addr = s_addr + (i * (1ul << shift));
6688c2ecf20Sopenharmony_ci		vpn = hpt_vpn(addr, vsid, ssize);
6698c2ecf20Sopenharmony_ci		hash = hpt_hash(vpn, shift, ssize);
6708c2ecf20Sopenharmony_ci		if (hidx & _PTEIDX_SECONDARY)
6718c2ecf20Sopenharmony_ci			hash = ~hash;
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_ci		slot = (hash & htab_hash_mask) * HPTES_PER_GROUP;
6748c2ecf20Sopenharmony_ci		slot += hidx & _PTEIDX_GROUP_IX;
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_ci		hptep = htab_address + slot;
6778c2ecf20Sopenharmony_ci		want_v = hpte_encode_avpn(vpn, psize, ssize);
6788c2ecf20Sopenharmony_ci		hpte_v = hpte_get_old_v(hptep);
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_ci		/* Even if we miss, we need to invalidate the TLB */
6818c2ecf20Sopenharmony_ci		if (HPTE_V_COMPARE(hpte_v, want_v) && (hpte_v & HPTE_V_VALID)) {
6828c2ecf20Sopenharmony_ci			/* recheck with locks held */
6838c2ecf20Sopenharmony_ci			native_lock_hpte(hptep);
6848c2ecf20Sopenharmony_ci			hpte_v = hpte_get_old_v(hptep);
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_ci			if (HPTE_V_COMPARE(hpte_v, want_v) && (hpte_v & HPTE_V_VALID)) {
6878c2ecf20Sopenharmony_ci				/*
6888c2ecf20Sopenharmony_ci				 * Invalidate the hpte. NOTE: this also unlocks it
6898c2ecf20Sopenharmony_ci				 */
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_ci				hptep->v = 0;
6928c2ecf20Sopenharmony_ci			} else
6938c2ecf20Sopenharmony_ci				native_unlock_hpte(hptep);
6948c2ecf20Sopenharmony_ci		}
6958c2ecf20Sopenharmony_ci		/*
6968c2ecf20Sopenharmony_ci		 * We need to do tlb invalidate for all the address, tlbie
6978c2ecf20Sopenharmony_ci		 * instruction compares entry_VA in tlb with the VA specified
6988c2ecf20Sopenharmony_ci		 * here
6998c2ecf20Sopenharmony_ci		 */
7008c2ecf20Sopenharmony_ci		tlbie(vpn, psize, actual_psize, ssize, local);
7018c2ecf20Sopenharmony_ci	}
7028c2ecf20Sopenharmony_ci	local_irq_restore(flags);
7038c2ecf20Sopenharmony_ci}
7048c2ecf20Sopenharmony_ci#else
7058c2ecf20Sopenharmony_cistatic void native_hugepage_invalidate(unsigned long vsid,
7068c2ecf20Sopenharmony_ci				       unsigned long addr,
7078c2ecf20Sopenharmony_ci				       unsigned char *hpte_slot_array,
7088c2ecf20Sopenharmony_ci				       int psize, int ssize, int local)
7098c2ecf20Sopenharmony_ci{
7108c2ecf20Sopenharmony_ci	WARN(1, "%s called without THP support\n", __func__);
7118c2ecf20Sopenharmony_ci}
7128c2ecf20Sopenharmony_ci#endif
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_cistatic void hpte_decode(struct hash_pte *hpte, unsigned long slot,
7158c2ecf20Sopenharmony_ci			int *psize, int *apsize, int *ssize, unsigned long *vpn)
7168c2ecf20Sopenharmony_ci{
7178c2ecf20Sopenharmony_ci	unsigned long avpn, pteg, vpi;
7188c2ecf20Sopenharmony_ci	unsigned long hpte_v = be64_to_cpu(hpte->v);
7198c2ecf20Sopenharmony_ci	unsigned long hpte_r = be64_to_cpu(hpte->r);
7208c2ecf20Sopenharmony_ci	unsigned long vsid, seg_off;
7218c2ecf20Sopenharmony_ci	int size, a_size, shift;
7228c2ecf20Sopenharmony_ci	/* Look at the 8 bit LP value */
7238c2ecf20Sopenharmony_ci	unsigned int lp = (hpte_r >> LP_SHIFT) & ((1 << LP_BITS) - 1);
7248c2ecf20Sopenharmony_ci
7258c2ecf20Sopenharmony_ci	if (cpu_has_feature(CPU_FTR_ARCH_300)) {
7268c2ecf20Sopenharmony_ci		hpte_v = hpte_new_to_old_v(hpte_v, hpte_r);
7278c2ecf20Sopenharmony_ci		hpte_r = hpte_new_to_old_r(hpte_r);
7288c2ecf20Sopenharmony_ci	}
7298c2ecf20Sopenharmony_ci	if (!(hpte_v & HPTE_V_LARGE)) {
7308c2ecf20Sopenharmony_ci		size   = MMU_PAGE_4K;
7318c2ecf20Sopenharmony_ci		a_size = MMU_PAGE_4K;
7328c2ecf20Sopenharmony_ci	} else {
7338c2ecf20Sopenharmony_ci		size = hpte_page_sizes[lp] & 0xf;
7348c2ecf20Sopenharmony_ci		a_size = hpte_page_sizes[lp] >> 4;
7358c2ecf20Sopenharmony_ci	}
7368c2ecf20Sopenharmony_ci	/* This works for all page sizes, and for 256M and 1T segments */
7378c2ecf20Sopenharmony_ci	*ssize = hpte_v >> HPTE_V_SSIZE_SHIFT;
7388c2ecf20Sopenharmony_ci	shift = mmu_psize_defs[size].shift;
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_ci	avpn = (HPTE_V_AVPN_VAL(hpte_v) & ~mmu_psize_defs[size].avpnm);
7418c2ecf20Sopenharmony_ci	pteg = slot / HPTES_PER_GROUP;
7428c2ecf20Sopenharmony_ci	if (hpte_v & HPTE_V_SECONDARY)
7438c2ecf20Sopenharmony_ci		pteg = ~pteg;
7448c2ecf20Sopenharmony_ci
7458c2ecf20Sopenharmony_ci	switch (*ssize) {
7468c2ecf20Sopenharmony_ci	case MMU_SEGSIZE_256M:
7478c2ecf20Sopenharmony_ci		/* We only have 28 - 23 bits of seg_off in avpn */
7488c2ecf20Sopenharmony_ci		seg_off = (avpn & 0x1f) << 23;
7498c2ecf20Sopenharmony_ci		vsid    =  avpn >> 5;
7508c2ecf20Sopenharmony_ci		/* We can find more bits from the pteg value */
7518c2ecf20Sopenharmony_ci		if (shift < 23) {
7528c2ecf20Sopenharmony_ci			vpi = (vsid ^ pteg) & htab_hash_mask;
7538c2ecf20Sopenharmony_ci			seg_off |= vpi << shift;
7548c2ecf20Sopenharmony_ci		}
7558c2ecf20Sopenharmony_ci		*vpn = vsid << (SID_SHIFT - VPN_SHIFT) | seg_off >> VPN_SHIFT;
7568c2ecf20Sopenharmony_ci		break;
7578c2ecf20Sopenharmony_ci	case MMU_SEGSIZE_1T:
7588c2ecf20Sopenharmony_ci		/* We only have 40 - 23 bits of seg_off in avpn */
7598c2ecf20Sopenharmony_ci		seg_off = (avpn & 0x1ffff) << 23;
7608c2ecf20Sopenharmony_ci		vsid    = avpn >> 17;
7618c2ecf20Sopenharmony_ci		if (shift < 23) {
7628c2ecf20Sopenharmony_ci			vpi = (vsid ^ (vsid << 25) ^ pteg) & htab_hash_mask;
7638c2ecf20Sopenharmony_ci			seg_off |= vpi << shift;
7648c2ecf20Sopenharmony_ci		}
7658c2ecf20Sopenharmony_ci		*vpn = vsid << (SID_SHIFT_1T - VPN_SHIFT) | seg_off >> VPN_SHIFT;
7668c2ecf20Sopenharmony_ci		break;
7678c2ecf20Sopenharmony_ci	default:
7688c2ecf20Sopenharmony_ci		*vpn = size = 0;
7698c2ecf20Sopenharmony_ci	}
7708c2ecf20Sopenharmony_ci	*psize  = size;
7718c2ecf20Sopenharmony_ci	*apsize = a_size;
7728c2ecf20Sopenharmony_ci}
7738c2ecf20Sopenharmony_ci
7748c2ecf20Sopenharmony_ci/*
7758c2ecf20Sopenharmony_ci * clear all mappings on kexec.  All cpus are in real mode (or they will
7768c2ecf20Sopenharmony_ci * be when they isi), and we are the only one left.  We rely on our kernel
7778c2ecf20Sopenharmony_ci * mapping being 0xC0's and the hardware ignoring those two real bits.
7788c2ecf20Sopenharmony_ci *
7798c2ecf20Sopenharmony_ci * This must be called with interrupts disabled.
7808c2ecf20Sopenharmony_ci *
7818c2ecf20Sopenharmony_ci * Taking the native_tlbie_lock is unsafe here due to the possibility of
7828c2ecf20Sopenharmony_ci * lockdep being on. On pre POWER5 hardware, not taking the lock could
7838c2ecf20Sopenharmony_ci * cause deadlock. POWER5 and newer not taking the lock is fine. This only
7848c2ecf20Sopenharmony_ci * gets called during boot before secondary CPUs have come up and during
7858c2ecf20Sopenharmony_ci * crashdump and all bets are off anyway.
7868c2ecf20Sopenharmony_ci *
7878c2ecf20Sopenharmony_ci * TODO: add batching support when enabled.  remember, no dynamic memory here,
7888c2ecf20Sopenharmony_ci * although there is the control page available...
7898c2ecf20Sopenharmony_ci */
7908c2ecf20Sopenharmony_cistatic void native_hpte_clear(void)
7918c2ecf20Sopenharmony_ci{
7928c2ecf20Sopenharmony_ci	unsigned long vpn = 0;
7938c2ecf20Sopenharmony_ci	unsigned long slot, slots;
7948c2ecf20Sopenharmony_ci	struct hash_pte *hptep = htab_address;
7958c2ecf20Sopenharmony_ci	unsigned long hpte_v;
7968c2ecf20Sopenharmony_ci	unsigned long pteg_count;
7978c2ecf20Sopenharmony_ci	int psize, apsize, ssize;
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_ci	pteg_count = htab_hash_mask + 1;
8008c2ecf20Sopenharmony_ci
8018c2ecf20Sopenharmony_ci	slots = pteg_count * HPTES_PER_GROUP;
8028c2ecf20Sopenharmony_ci
8038c2ecf20Sopenharmony_ci	for (slot = 0; slot < slots; slot++, hptep++) {
8048c2ecf20Sopenharmony_ci		/*
8058c2ecf20Sopenharmony_ci		 * we could lock the pte here, but we are the only cpu
8068c2ecf20Sopenharmony_ci		 * running,  right?  and for crash dump, we probably
8078c2ecf20Sopenharmony_ci		 * don't want to wait for a maybe bad cpu.
8088c2ecf20Sopenharmony_ci		 */
8098c2ecf20Sopenharmony_ci		hpte_v = be64_to_cpu(hptep->v);
8108c2ecf20Sopenharmony_ci
8118c2ecf20Sopenharmony_ci		/*
8128c2ecf20Sopenharmony_ci		 * Call __tlbie() here rather than tlbie() since we can't take the
8138c2ecf20Sopenharmony_ci		 * native_tlbie_lock.
8148c2ecf20Sopenharmony_ci		 */
8158c2ecf20Sopenharmony_ci		if (hpte_v & HPTE_V_VALID) {
8168c2ecf20Sopenharmony_ci			hpte_decode(hptep, slot, &psize, &apsize, &ssize, &vpn);
8178c2ecf20Sopenharmony_ci			hptep->v = 0;
8188c2ecf20Sopenharmony_ci			___tlbie(vpn, psize, apsize, ssize);
8198c2ecf20Sopenharmony_ci		}
8208c2ecf20Sopenharmony_ci	}
8218c2ecf20Sopenharmony_ci
8228c2ecf20Sopenharmony_ci	asm volatile("eieio; tlbsync; ptesync":::"memory");
8238c2ecf20Sopenharmony_ci}
8248c2ecf20Sopenharmony_ci
8258c2ecf20Sopenharmony_ci/*
8268c2ecf20Sopenharmony_ci * Batched hash table flush, we batch the tlbie's to avoid taking/releasing
8278c2ecf20Sopenharmony_ci * the lock all the time
8288c2ecf20Sopenharmony_ci */
8298c2ecf20Sopenharmony_cistatic void native_flush_hash_range(unsigned long number, int local)
8308c2ecf20Sopenharmony_ci{
8318c2ecf20Sopenharmony_ci	unsigned long vpn = 0;
8328c2ecf20Sopenharmony_ci	unsigned long hash, index, hidx, shift, slot;
8338c2ecf20Sopenharmony_ci	struct hash_pte *hptep;
8348c2ecf20Sopenharmony_ci	unsigned long hpte_v;
8358c2ecf20Sopenharmony_ci	unsigned long want_v;
8368c2ecf20Sopenharmony_ci	unsigned long flags;
8378c2ecf20Sopenharmony_ci	real_pte_t pte;
8388c2ecf20Sopenharmony_ci	struct ppc64_tlb_batch *batch = this_cpu_ptr(&ppc64_tlb_batch);
8398c2ecf20Sopenharmony_ci	unsigned long psize = batch->psize;
8408c2ecf20Sopenharmony_ci	int ssize = batch->ssize;
8418c2ecf20Sopenharmony_ci	int i;
8428c2ecf20Sopenharmony_ci	unsigned int use_local;
8438c2ecf20Sopenharmony_ci
8448c2ecf20Sopenharmony_ci	use_local = local && mmu_has_feature(MMU_FTR_TLBIEL) &&
8458c2ecf20Sopenharmony_ci		mmu_psize_defs[psize].tlbiel && !cxl_ctx_in_use();
8468c2ecf20Sopenharmony_ci
8478c2ecf20Sopenharmony_ci	local_irq_save(flags);
8488c2ecf20Sopenharmony_ci
8498c2ecf20Sopenharmony_ci	for (i = 0; i < number; i++) {
8508c2ecf20Sopenharmony_ci		vpn = batch->vpn[i];
8518c2ecf20Sopenharmony_ci		pte = batch->pte[i];
8528c2ecf20Sopenharmony_ci
8538c2ecf20Sopenharmony_ci		pte_iterate_hashed_subpages(pte, psize, vpn, index, shift) {
8548c2ecf20Sopenharmony_ci			hash = hpt_hash(vpn, shift, ssize);
8558c2ecf20Sopenharmony_ci			hidx = __rpte_to_hidx(pte, index);
8568c2ecf20Sopenharmony_ci			if (hidx & _PTEIDX_SECONDARY)
8578c2ecf20Sopenharmony_ci				hash = ~hash;
8588c2ecf20Sopenharmony_ci			slot = (hash & htab_hash_mask) * HPTES_PER_GROUP;
8598c2ecf20Sopenharmony_ci			slot += hidx & _PTEIDX_GROUP_IX;
8608c2ecf20Sopenharmony_ci			hptep = htab_address + slot;
8618c2ecf20Sopenharmony_ci			want_v = hpte_encode_avpn(vpn, psize, ssize);
8628c2ecf20Sopenharmony_ci			hpte_v = hpte_get_old_v(hptep);
8638c2ecf20Sopenharmony_ci
8648c2ecf20Sopenharmony_ci			if (!HPTE_V_COMPARE(hpte_v, want_v) || !(hpte_v & HPTE_V_VALID))
8658c2ecf20Sopenharmony_ci				continue;
8668c2ecf20Sopenharmony_ci			/* lock and try again */
8678c2ecf20Sopenharmony_ci			native_lock_hpte(hptep);
8688c2ecf20Sopenharmony_ci			hpte_v = hpte_get_old_v(hptep);
8698c2ecf20Sopenharmony_ci
8708c2ecf20Sopenharmony_ci			if (!HPTE_V_COMPARE(hpte_v, want_v) || !(hpte_v & HPTE_V_VALID))
8718c2ecf20Sopenharmony_ci				native_unlock_hpte(hptep);
8728c2ecf20Sopenharmony_ci			else
8738c2ecf20Sopenharmony_ci				hptep->v = 0;
8748c2ecf20Sopenharmony_ci
8758c2ecf20Sopenharmony_ci		} pte_iterate_hashed_end();
8768c2ecf20Sopenharmony_ci	}
8778c2ecf20Sopenharmony_ci
8788c2ecf20Sopenharmony_ci	if (use_local) {
8798c2ecf20Sopenharmony_ci		asm volatile("ptesync":::"memory");
8808c2ecf20Sopenharmony_ci		for (i = 0; i < number; i++) {
8818c2ecf20Sopenharmony_ci			vpn = batch->vpn[i];
8828c2ecf20Sopenharmony_ci			pte = batch->pte[i];
8838c2ecf20Sopenharmony_ci
8848c2ecf20Sopenharmony_ci			pte_iterate_hashed_subpages(pte, psize,
8858c2ecf20Sopenharmony_ci						    vpn, index, shift) {
8868c2ecf20Sopenharmony_ci				__tlbiel(vpn, psize, psize, ssize);
8878c2ecf20Sopenharmony_ci			} pte_iterate_hashed_end();
8888c2ecf20Sopenharmony_ci		}
8898c2ecf20Sopenharmony_ci		ppc_after_tlbiel_barrier();
8908c2ecf20Sopenharmony_ci	} else {
8918c2ecf20Sopenharmony_ci		int lock_tlbie = !mmu_has_feature(MMU_FTR_LOCKLESS_TLBIE);
8928c2ecf20Sopenharmony_ci
8938c2ecf20Sopenharmony_ci		if (lock_tlbie)
8948c2ecf20Sopenharmony_ci			raw_spin_lock(&native_tlbie_lock);
8958c2ecf20Sopenharmony_ci
8968c2ecf20Sopenharmony_ci		asm volatile("ptesync":::"memory");
8978c2ecf20Sopenharmony_ci		for (i = 0; i < number; i++) {
8988c2ecf20Sopenharmony_ci			vpn = batch->vpn[i];
8998c2ecf20Sopenharmony_ci			pte = batch->pte[i];
9008c2ecf20Sopenharmony_ci
9018c2ecf20Sopenharmony_ci			pte_iterate_hashed_subpages(pte, psize,
9028c2ecf20Sopenharmony_ci						    vpn, index, shift) {
9038c2ecf20Sopenharmony_ci				__tlbie(vpn, psize, psize, ssize);
9048c2ecf20Sopenharmony_ci			} pte_iterate_hashed_end();
9058c2ecf20Sopenharmony_ci		}
9068c2ecf20Sopenharmony_ci		/*
9078c2ecf20Sopenharmony_ci		 * Just do one more with the last used values.
9088c2ecf20Sopenharmony_ci		 */
9098c2ecf20Sopenharmony_ci		fixup_tlbie_vpn(vpn, psize, psize, ssize);
9108c2ecf20Sopenharmony_ci		asm volatile("eieio; tlbsync; ptesync":::"memory");
9118c2ecf20Sopenharmony_ci
9128c2ecf20Sopenharmony_ci		if (lock_tlbie)
9138c2ecf20Sopenharmony_ci			raw_spin_unlock(&native_tlbie_lock);
9148c2ecf20Sopenharmony_ci	}
9158c2ecf20Sopenharmony_ci
9168c2ecf20Sopenharmony_ci	local_irq_restore(flags);
9178c2ecf20Sopenharmony_ci}
9188c2ecf20Sopenharmony_ci
9198c2ecf20Sopenharmony_civoid __init hpte_init_native(void)
9208c2ecf20Sopenharmony_ci{
9218c2ecf20Sopenharmony_ci	mmu_hash_ops.hpte_invalidate	= native_hpte_invalidate;
9228c2ecf20Sopenharmony_ci	mmu_hash_ops.hpte_updatepp	= native_hpte_updatepp;
9238c2ecf20Sopenharmony_ci	mmu_hash_ops.hpte_updateboltedpp = native_hpte_updateboltedpp;
9248c2ecf20Sopenharmony_ci	mmu_hash_ops.hpte_removebolted = native_hpte_removebolted;
9258c2ecf20Sopenharmony_ci	mmu_hash_ops.hpte_insert	= native_hpte_insert;
9268c2ecf20Sopenharmony_ci	mmu_hash_ops.hpte_remove	= native_hpte_remove;
9278c2ecf20Sopenharmony_ci	mmu_hash_ops.hpte_clear_all	= native_hpte_clear;
9288c2ecf20Sopenharmony_ci	mmu_hash_ops.flush_hash_range = native_flush_hash_range;
9298c2ecf20Sopenharmony_ci	mmu_hash_ops.hugepage_invalidate   = native_hugepage_invalidate;
9308c2ecf20Sopenharmony_ci}
931