162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * native hashtable management.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * SMP scalability work:
662306a36Sopenharmony_ci *    Copyright (C) 2001 Anton Blanchard <anton@au.ibm.com>, IBM
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#undef DEBUG_LOW
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/spinlock.h>
1262306a36Sopenharmony_ci#include <linux/bitops.h>
1362306a36Sopenharmony_ci#include <linux/of.h>
1462306a36Sopenharmony_ci#include <linux/processor.h>
1562306a36Sopenharmony_ci#include <linux/threads.h>
1662306a36Sopenharmony_ci#include <linux/smp.h>
1762306a36Sopenharmony_ci#include <linux/pgtable.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include <asm/machdep.h>
2062306a36Sopenharmony_ci#include <asm/mmu.h>
2162306a36Sopenharmony_ci#include <asm/mmu_context.h>
2262306a36Sopenharmony_ci#include <asm/trace.h>
2362306a36Sopenharmony_ci#include <asm/tlb.h>
2462306a36Sopenharmony_ci#include <asm/cputable.h>
2562306a36Sopenharmony_ci#include <asm/udbg.h>
2662306a36Sopenharmony_ci#include <asm/kexec.h>
2762306a36Sopenharmony_ci#include <asm/ppc-opcode.h>
2862306a36Sopenharmony_ci#include <asm/feature-fixups.h>
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#include <misc/cxl-base.h>
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci#ifdef DEBUG_LOW
3362306a36Sopenharmony_ci#define DBG_LOW(fmt...) udbg_printf(fmt)
3462306a36Sopenharmony_ci#else
3562306a36Sopenharmony_ci#define DBG_LOW(fmt...)
3662306a36Sopenharmony_ci#endif
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci#ifdef __BIG_ENDIAN__
3962306a36Sopenharmony_ci#define HPTE_LOCK_BIT 3
4062306a36Sopenharmony_ci#else
4162306a36Sopenharmony_ci#define HPTE_LOCK_BIT (56+3)
4262306a36Sopenharmony_ci#endif
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistatic DEFINE_RAW_SPINLOCK(native_tlbie_lock);
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci#ifdef CONFIG_LOCKDEP
4762306a36Sopenharmony_cistatic struct lockdep_map hpte_lock_map =
4862306a36Sopenharmony_ci	STATIC_LOCKDEP_MAP_INIT("hpte_lock", &hpte_lock_map);
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_cistatic void acquire_hpte_lock(void)
5162306a36Sopenharmony_ci{
5262306a36Sopenharmony_ci	lock_map_acquire(&hpte_lock_map);
5362306a36Sopenharmony_ci}
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_cistatic void release_hpte_lock(void)
5662306a36Sopenharmony_ci{
5762306a36Sopenharmony_ci	lock_map_release(&hpte_lock_map);
5862306a36Sopenharmony_ci}
5962306a36Sopenharmony_ci#else
6062306a36Sopenharmony_cistatic void acquire_hpte_lock(void)
6162306a36Sopenharmony_ci{
6262306a36Sopenharmony_ci}
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cistatic void release_hpte_lock(void)
6562306a36Sopenharmony_ci{
6662306a36Sopenharmony_ci}
6762306a36Sopenharmony_ci#endif
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_cistatic inline unsigned long  ___tlbie(unsigned long vpn, int psize,
7062306a36Sopenharmony_ci						int apsize, int ssize)
7162306a36Sopenharmony_ci{
7262306a36Sopenharmony_ci	unsigned long va;
7362306a36Sopenharmony_ci	unsigned int penc;
7462306a36Sopenharmony_ci	unsigned long sllp;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	/*
7762306a36Sopenharmony_ci	 * We need 14 to 65 bits of va for a tlibe of 4K page
7862306a36Sopenharmony_ci	 * With vpn we ignore the lower VPN_SHIFT bits already.
7962306a36Sopenharmony_ci	 * And top two bits are already ignored because we can
8062306a36Sopenharmony_ci	 * only accomodate 76 bits in a 64 bit vpn with a VPN_SHIFT
8162306a36Sopenharmony_ci	 * of 12.
8262306a36Sopenharmony_ci	 */
8362306a36Sopenharmony_ci	va = vpn << VPN_SHIFT;
8462306a36Sopenharmony_ci	/*
8562306a36Sopenharmony_ci	 * clear top 16 bits of 64bit va, non SLS segment
8662306a36Sopenharmony_ci	 * Older versions of the architecture (2.02 and earler) require the
8762306a36Sopenharmony_ci	 * masking of the top 16 bits.
8862306a36Sopenharmony_ci	 */
8962306a36Sopenharmony_ci	if (mmu_has_feature(MMU_FTR_TLBIE_CROP_VA))
9062306a36Sopenharmony_ci		va &= ~(0xffffULL << 48);
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	switch (psize) {
9362306a36Sopenharmony_ci	case MMU_PAGE_4K:
9462306a36Sopenharmony_ci		/* clear out bits after (52) [0....52.....63] */
9562306a36Sopenharmony_ci		va &= ~((1ul << (64 - 52)) - 1);
9662306a36Sopenharmony_ci		va |= ssize << 8;
9762306a36Sopenharmony_ci		sllp = get_sllp_encoding(apsize);
9862306a36Sopenharmony_ci		va |= sllp << 5;
9962306a36Sopenharmony_ci		asm volatile(ASM_FTR_IFCLR("tlbie %0,0", PPC_TLBIE(%1,%0), %2)
10062306a36Sopenharmony_ci			     : : "r" (va), "r"(0), "i" (CPU_FTR_ARCH_206)
10162306a36Sopenharmony_ci			     : "memory");
10262306a36Sopenharmony_ci		break;
10362306a36Sopenharmony_ci	default:
10462306a36Sopenharmony_ci		/* We need 14 to 14 + i bits of va */
10562306a36Sopenharmony_ci		penc = mmu_psize_defs[psize].penc[apsize];
10662306a36Sopenharmony_ci		va &= ~((1ul << mmu_psize_defs[apsize].shift) - 1);
10762306a36Sopenharmony_ci		va |= penc << 12;
10862306a36Sopenharmony_ci		va |= ssize << 8;
10962306a36Sopenharmony_ci		/*
11062306a36Sopenharmony_ci		 * AVAL bits:
11162306a36Sopenharmony_ci		 * We don't need all the bits, but rest of the bits
11262306a36Sopenharmony_ci		 * must be ignored by the processor.
11362306a36Sopenharmony_ci		 * vpn cover upto 65 bits of va. (0...65) and we need
11462306a36Sopenharmony_ci		 * 58..64 bits of va.
11562306a36Sopenharmony_ci		 */
11662306a36Sopenharmony_ci		va |= (vpn & 0xfe); /* AVAL */
11762306a36Sopenharmony_ci		va |= 1; /* L */
11862306a36Sopenharmony_ci		asm volatile(ASM_FTR_IFCLR("tlbie %0,1", PPC_TLBIE(%1,%0), %2)
11962306a36Sopenharmony_ci			     : : "r" (va), "r"(0), "i" (CPU_FTR_ARCH_206)
12062306a36Sopenharmony_ci			     : "memory");
12162306a36Sopenharmony_ci		break;
12262306a36Sopenharmony_ci	}
12362306a36Sopenharmony_ci	return va;
12462306a36Sopenharmony_ci}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_cistatic inline void fixup_tlbie_vpn(unsigned long vpn, int psize,
12762306a36Sopenharmony_ci				   int apsize, int ssize)
12862306a36Sopenharmony_ci{
12962306a36Sopenharmony_ci	if (cpu_has_feature(CPU_FTR_P9_TLBIE_ERAT_BUG)) {
13062306a36Sopenharmony_ci		/* Radix flush for a hash guest */
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci		unsigned long rb,rs,prs,r,ric;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci		rb = PPC_BIT(52); /* IS = 2 */
13562306a36Sopenharmony_ci		rs = 0;  /* lpid = 0 */
13662306a36Sopenharmony_ci		prs = 0; /* partition scoped */
13762306a36Sopenharmony_ci		r = 1;   /* radix format */
13862306a36Sopenharmony_ci		ric = 0; /* RIC_FLSUH_TLB */
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci		/*
14162306a36Sopenharmony_ci		 * Need the extra ptesync to make sure we don't
14262306a36Sopenharmony_ci		 * re-order the tlbie
14362306a36Sopenharmony_ci		 */
14462306a36Sopenharmony_ci		asm volatile("ptesync": : :"memory");
14562306a36Sopenharmony_ci		asm volatile(PPC_TLBIE_5(%0, %4, %3, %2, %1)
14662306a36Sopenharmony_ci			     : : "r"(rb), "i"(r), "i"(prs),
14762306a36Sopenharmony_ci			       "i"(ric), "r"(rs) : "memory");
14862306a36Sopenharmony_ci	}
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	if (cpu_has_feature(CPU_FTR_P9_TLBIE_STQ_BUG)) {
15262306a36Sopenharmony_ci		/* Need the extra ptesync to ensure we don't reorder tlbie*/
15362306a36Sopenharmony_ci		asm volatile("ptesync": : :"memory");
15462306a36Sopenharmony_ci		___tlbie(vpn, psize, apsize, ssize);
15562306a36Sopenharmony_ci	}
15662306a36Sopenharmony_ci}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_cistatic inline void __tlbie(unsigned long vpn, int psize, int apsize, int ssize)
15962306a36Sopenharmony_ci{
16062306a36Sopenharmony_ci	unsigned long rb;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	rb = ___tlbie(vpn, psize, apsize, ssize);
16362306a36Sopenharmony_ci	trace_tlbie(0, 0, rb, 0, 0, 0, 0);
16462306a36Sopenharmony_ci}
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_cistatic inline void __tlbiel(unsigned long vpn, int psize, int apsize, int ssize)
16762306a36Sopenharmony_ci{
16862306a36Sopenharmony_ci	unsigned long va;
16962306a36Sopenharmony_ci	unsigned int penc;
17062306a36Sopenharmony_ci	unsigned long sllp;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	/* VPN_SHIFT can be atmost 12 */
17362306a36Sopenharmony_ci	va = vpn << VPN_SHIFT;
17462306a36Sopenharmony_ci	/*
17562306a36Sopenharmony_ci	 * clear top 16 bits of 64 bit va, non SLS segment
17662306a36Sopenharmony_ci	 * Older versions of the architecture (2.02 and earler) require the
17762306a36Sopenharmony_ci	 * masking of the top 16 bits.
17862306a36Sopenharmony_ci	 */
17962306a36Sopenharmony_ci	if (mmu_has_feature(MMU_FTR_TLBIE_CROP_VA))
18062306a36Sopenharmony_ci		va &= ~(0xffffULL << 48);
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	switch (psize) {
18362306a36Sopenharmony_ci	case MMU_PAGE_4K:
18462306a36Sopenharmony_ci		/* clear out bits after(52) [0....52.....63] */
18562306a36Sopenharmony_ci		va &= ~((1ul << (64 - 52)) - 1);
18662306a36Sopenharmony_ci		va |= ssize << 8;
18762306a36Sopenharmony_ci		sllp = get_sllp_encoding(apsize);
18862306a36Sopenharmony_ci		va |= sllp << 5;
18962306a36Sopenharmony_ci		asm volatile(ASM_FTR_IFSET("tlbiel %0", PPC_TLBIEL_v205(%0, 0), %1)
19062306a36Sopenharmony_ci			     : : "r" (va), "i" (CPU_FTR_ARCH_206)
19162306a36Sopenharmony_ci			     : "memory");
19262306a36Sopenharmony_ci		break;
19362306a36Sopenharmony_ci	default:
19462306a36Sopenharmony_ci		/* We need 14 to 14 + i bits of va */
19562306a36Sopenharmony_ci		penc = mmu_psize_defs[psize].penc[apsize];
19662306a36Sopenharmony_ci		va &= ~((1ul << mmu_psize_defs[apsize].shift) - 1);
19762306a36Sopenharmony_ci		va |= penc << 12;
19862306a36Sopenharmony_ci		va |= ssize << 8;
19962306a36Sopenharmony_ci		/*
20062306a36Sopenharmony_ci		 * AVAL bits:
20162306a36Sopenharmony_ci		 * We don't need all the bits, but rest of the bits
20262306a36Sopenharmony_ci		 * must be ignored by the processor.
20362306a36Sopenharmony_ci		 * vpn cover upto 65 bits of va. (0...65) and we need
20462306a36Sopenharmony_ci		 * 58..64 bits of va.
20562306a36Sopenharmony_ci		 */
20662306a36Sopenharmony_ci		va |= (vpn & 0xfe);
20762306a36Sopenharmony_ci		va |= 1; /* L */
20862306a36Sopenharmony_ci		asm volatile(ASM_FTR_IFSET("tlbiel %0", PPC_TLBIEL_v205(%0, 1), %1)
20962306a36Sopenharmony_ci			     : : "r" (va), "i" (CPU_FTR_ARCH_206)
21062306a36Sopenharmony_ci			     : "memory");
21162306a36Sopenharmony_ci		break;
21262306a36Sopenharmony_ci	}
21362306a36Sopenharmony_ci	trace_tlbie(0, 1, va, 0, 0, 0, 0);
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci}
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_cistatic inline void tlbie(unsigned long vpn, int psize, int apsize,
21862306a36Sopenharmony_ci			 int ssize, int local)
21962306a36Sopenharmony_ci{
22062306a36Sopenharmony_ci	unsigned int use_local;
22162306a36Sopenharmony_ci	int lock_tlbie = !mmu_has_feature(MMU_FTR_LOCKLESS_TLBIE);
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	use_local = local && mmu_has_feature(MMU_FTR_TLBIEL) && !cxl_ctx_in_use();
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	if (use_local)
22662306a36Sopenharmony_ci		use_local = mmu_psize_defs[psize].tlbiel;
22762306a36Sopenharmony_ci	if (lock_tlbie && !use_local)
22862306a36Sopenharmony_ci		raw_spin_lock(&native_tlbie_lock);
22962306a36Sopenharmony_ci	asm volatile("ptesync": : :"memory");
23062306a36Sopenharmony_ci	if (use_local) {
23162306a36Sopenharmony_ci		__tlbiel(vpn, psize, apsize, ssize);
23262306a36Sopenharmony_ci		ppc_after_tlbiel_barrier();
23362306a36Sopenharmony_ci	} else {
23462306a36Sopenharmony_ci		__tlbie(vpn, psize, apsize, ssize);
23562306a36Sopenharmony_ci		fixup_tlbie_vpn(vpn, psize, apsize, ssize);
23662306a36Sopenharmony_ci		asm volatile("eieio; tlbsync; ptesync": : :"memory");
23762306a36Sopenharmony_ci	}
23862306a36Sopenharmony_ci	if (lock_tlbie && !use_local)
23962306a36Sopenharmony_ci		raw_spin_unlock(&native_tlbie_lock);
24062306a36Sopenharmony_ci}
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_cistatic inline void native_lock_hpte(struct hash_pte *hptep)
24362306a36Sopenharmony_ci{
24462306a36Sopenharmony_ci	unsigned long *word = (unsigned long *)&hptep->v;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	acquire_hpte_lock();
24762306a36Sopenharmony_ci	while (1) {
24862306a36Sopenharmony_ci		if (!test_and_set_bit_lock(HPTE_LOCK_BIT, word))
24962306a36Sopenharmony_ci			break;
25062306a36Sopenharmony_ci		spin_begin();
25162306a36Sopenharmony_ci		while(test_bit(HPTE_LOCK_BIT, word))
25262306a36Sopenharmony_ci			spin_cpu_relax();
25362306a36Sopenharmony_ci		spin_end();
25462306a36Sopenharmony_ci	}
25562306a36Sopenharmony_ci}
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_cistatic inline void native_unlock_hpte(struct hash_pte *hptep)
25862306a36Sopenharmony_ci{
25962306a36Sopenharmony_ci	unsigned long *word = (unsigned long *)&hptep->v;
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	release_hpte_lock();
26262306a36Sopenharmony_ci	clear_bit_unlock(HPTE_LOCK_BIT, word);
26362306a36Sopenharmony_ci}
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_cistatic long native_hpte_insert(unsigned long hpte_group, unsigned long vpn,
26662306a36Sopenharmony_ci			unsigned long pa, unsigned long rflags,
26762306a36Sopenharmony_ci			unsigned long vflags, int psize, int apsize, int ssize)
26862306a36Sopenharmony_ci{
26962306a36Sopenharmony_ci	struct hash_pte *hptep = htab_address + hpte_group;
27062306a36Sopenharmony_ci	unsigned long hpte_v, hpte_r;
27162306a36Sopenharmony_ci	unsigned long flags;
27262306a36Sopenharmony_ci	int i;
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	local_irq_save(flags);
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	if (!(vflags & HPTE_V_BOLTED)) {
27762306a36Sopenharmony_ci		DBG_LOW("    insert(group=%lx, vpn=%016lx, pa=%016lx,"
27862306a36Sopenharmony_ci			" rflags=%lx, vflags=%lx, psize=%d)\n",
27962306a36Sopenharmony_ci			hpte_group, vpn, pa, rflags, vflags, psize);
28062306a36Sopenharmony_ci	}
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	for (i = 0; i < HPTES_PER_GROUP; i++) {
28362306a36Sopenharmony_ci		if (! (be64_to_cpu(hptep->v) & HPTE_V_VALID)) {
28462306a36Sopenharmony_ci			/* retry with lock held */
28562306a36Sopenharmony_ci			native_lock_hpte(hptep);
28662306a36Sopenharmony_ci			if (! (be64_to_cpu(hptep->v) & HPTE_V_VALID))
28762306a36Sopenharmony_ci				break;
28862306a36Sopenharmony_ci			native_unlock_hpte(hptep);
28962306a36Sopenharmony_ci		}
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci		hptep++;
29262306a36Sopenharmony_ci	}
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	if (i == HPTES_PER_GROUP) {
29562306a36Sopenharmony_ci		local_irq_restore(flags);
29662306a36Sopenharmony_ci		return -1;
29762306a36Sopenharmony_ci	}
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	hpte_v = hpte_encode_v(vpn, psize, apsize, ssize) | vflags | HPTE_V_VALID;
30062306a36Sopenharmony_ci	hpte_r = hpte_encode_r(pa, psize, apsize) | rflags;
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	if (!(vflags & HPTE_V_BOLTED)) {
30362306a36Sopenharmony_ci		DBG_LOW(" i=%x hpte_v=%016lx, hpte_r=%016lx\n",
30462306a36Sopenharmony_ci			i, hpte_v, hpte_r);
30562306a36Sopenharmony_ci	}
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	if (cpu_has_feature(CPU_FTR_ARCH_300)) {
30862306a36Sopenharmony_ci		hpte_r = hpte_old_to_new_r(hpte_v, hpte_r);
30962306a36Sopenharmony_ci		hpte_v = hpte_old_to_new_v(hpte_v);
31062306a36Sopenharmony_ci	}
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	hptep->r = cpu_to_be64(hpte_r);
31362306a36Sopenharmony_ci	/* Guarantee the second dword is visible before the valid bit */
31462306a36Sopenharmony_ci	eieio();
31562306a36Sopenharmony_ci	/*
31662306a36Sopenharmony_ci	 * Now set the first dword including the valid bit
31762306a36Sopenharmony_ci	 * NOTE: this also unlocks the hpte
31862306a36Sopenharmony_ci	 */
31962306a36Sopenharmony_ci	release_hpte_lock();
32062306a36Sopenharmony_ci	hptep->v = cpu_to_be64(hpte_v);
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	__asm__ __volatile__ ("ptesync" : : : "memory");
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	local_irq_restore(flags);
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	return i | (!!(vflags & HPTE_V_SECONDARY) << 3);
32762306a36Sopenharmony_ci}
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_cistatic long native_hpte_remove(unsigned long hpte_group)
33062306a36Sopenharmony_ci{
33162306a36Sopenharmony_ci	unsigned long hpte_v, flags;
33262306a36Sopenharmony_ci	struct hash_pte *hptep;
33362306a36Sopenharmony_ci	int i;
33462306a36Sopenharmony_ci	int slot_offset;
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	local_irq_save(flags);
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	DBG_LOW("    remove(group=%lx)\n", hpte_group);
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	/* pick a random entry to start at */
34162306a36Sopenharmony_ci	slot_offset = mftb() & 0x7;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	for (i = 0; i < HPTES_PER_GROUP; i++) {
34462306a36Sopenharmony_ci		hptep = htab_address + hpte_group + slot_offset;
34562306a36Sopenharmony_ci		hpte_v = be64_to_cpu(hptep->v);
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci		if ((hpte_v & HPTE_V_VALID) && !(hpte_v & HPTE_V_BOLTED)) {
34862306a36Sopenharmony_ci			/* retry with lock held */
34962306a36Sopenharmony_ci			native_lock_hpte(hptep);
35062306a36Sopenharmony_ci			hpte_v = be64_to_cpu(hptep->v);
35162306a36Sopenharmony_ci			if ((hpte_v & HPTE_V_VALID)
35262306a36Sopenharmony_ci			    && !(hpte_v & HPTE_V_BOLTED))
35362306a36Sopenharmony_ci				break;
35462306a36Sopenharmony_ci			native_unlock_hpte(hptep);
35562306a36Sopenharmony_ci		}
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci		slot_offset++;
35862306a36Sopenharmony_ci		slot_offset &= 0x7;
35962306a36Sopenharmony_ci	}
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	if (i == HPTES_PER_GROUP) {
36262306a36Sopenharmony_ci		i = -1;
36362306a36Sopenharmony_ci		goto out;
36462306a36Sopenharmony_ci	}
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	/* Invalidate the hpte. NOTE: this also unlocks it */
36762306a36Sopenharmony_ci	release_hpte_lock();
36862306a36Sopenharmony_ci	hptep->v = 0;
36962306a36Sopenharmony_ciout:
37062306a36Sopenharmony_ci	local_irq_restore(flags);
37162306a36Sopenharmony_ci	return i;
37262306a36Sopenharmony_ci}
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_cistatic long native_hpte_updatepp(unsigned long slot, unsigned long newpp,
37562306a36Sopenharmony_ci				 unsigned long vpn, int bpsize,
37662306a36Sopenharmony_ci				 int apsize, int ssize, unsigned long flags)
37762306a36Sopenharmony_ci{
37862306a36Sopenharmony_ci	struct hash_pte *hptep = htab_address + slot;
37962306a36Sopenharmony_ci	unsigned long hpte_v, want_v;
38062306a36Sopenharmony_ci	int ret = 0, local = 0;
38162306a36Sopenharmony_ci	unsigned long irqflags;
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	local_irq_save(irqflags);
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	want_v = hpte_encode_avpn(vpn, bpsize, ssize);
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	DBG_LOW("    update(vpn=%016lx, avpnv=%016lx, group=%lx, newpp=%lx)",
38862306a36Sopenharmony_ci		vpn, want_v & HPTE_V_AVPN, slot, newpp);
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	hpte_v = hpte_get_old_v(hptep);
39162306a36Sopenharmony_ci	/*
39262306a36Sopenharmony_ci	 * We need to invalidate the TLB always because hpte_remove doesn't do
39362306a36Sopenharmony_ci	 * a tlb invalidate. If a hash bucket gets full, we "evict" a more/less
39462306a36Sopenharmony_ci	 * random entry from it. When we do that we don't invalidate the TLB
39562306a36Sopenharmony_ci	 * (hpte_remove) because we assume the old translation is still
39662306a36Sopenharmony_ci	 * technically "valid".
39762306a36Sopenharmony_ci	 */
39862306a36Sopenharmony_ci	if (!HPTE_V_COMPARE(hpte_v, want_v) || !(hpte_v & HPTE_V_VALID)) {
39962306a36Sopenharmony_ci		DBG_LOW(" -> miss\n");
40062306a36Sopenharmony_ci		ret = -1;
40162306a36Sopenharmony_ci	} else {
40262306a36Sopenharmony_ci		native_lock_hpte(hptep);
40362306a36Sopenharmony_ci		/* recheck with locks held */
40462306a36Sopenharmony_ci		hpte_v = hpte_get_old_v(hptep);
40562306a36Sopenharmony_ci		if (unlikely(!HPTE_V_COMPARE(hpte_v, want_v) ||
40662306a36Sopenharmony_ci			     !(hpte_v & HPTE_V_VALID))) {
40762306a36Sopenharmony_ci			ret = -1;
40862306a36Sopenharmony_ci		} else {
40962306a36Sopenharmony_ci			DBG_LOW(" -> hit\n");
41062306a36Sopenharmony_ci			/* Update the HPTE */
41162306a36Sopenharmony_ci			hptep->r = cpu_to_be64((be64_to_cpu(hptep->r) &
41262306a36Sopenharmony_ci						~(HPTE_R_PPP | HPTE_R_N)) |
41362306a36Sopenharmony_ci					       (newpp & (HPTE_R_PPP | HPTE_R_N |
41462306a36Sopenharmony_ci							 HPTE_R_C)));
41562306a36Sopenharmony_ci		}
41662306a36Sopenharmony_ci		native_unlock_hpte(hptep);
41762306a36Sopenharmony_ci	}
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	if (flags & HPTE_LOCAL_UPDATE)
42062306a36Sopenharmony_ci		local = 1;
42162306a36Sopenharmony_ci	/*
42262306a36Sopenharmony_ci	 * Ensure it is out of the tlb too if it is not a nohpte fault
42362306a36Sopenharmony_ci	 */
42462306a36Sopenharmony_ci	if (!(flags & HPTE_NOHPTE_UPDATE))
42562306a36Sopenharmony_ci		tlbie(vpn, bpsize, apsize, ssize, local);
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	local_irq_restore(irqflags);
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	return ret;
43062306a36Sopenharmony_ci}
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_cistatic long __native_hpte_find(unsigned long want_v, unsigned long slot)
43362306a36Sopenharmony_ci{
43462306a36Sopenharmony_ci	struct hash_pte *hptep;
43562306a36Sopenharmony_ci	unsigned long hpte_v;
43662306a36Sopenharmony_ci	unsigned long i;
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	for (i = 0; i < HPTES_PER_GROUP; i++) {
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci		hptep = htab_address + slot;
44162306a36Sopenharmony_ci		hpte_v = hpte_get_old_v(hptep);
44262306a36Sopenharmony_ci		if (HPTE_V_COMPARE(hpte_v, want_v) && (hpte_v & HPTE_V_VALID))
44362306a36Sopenharmony_ci			/* HPTE matches */
44462306a36Sopenharmony_ci			return slot;
44562306a36Sopenharmony_ci		++slot;
44662306a36Sopenharmony_ci	}
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	return -1;
44962306a36Sopenharmony_ci}
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_cistatic long native_hpte_find(unsigned long vpn, int psize, int ssize)
45262306a36Sopenharmony_ci{
45362306a36Sopenharmony_ci	unsigned long hpte_group;
45462306a36Sopenharmony_ci	unsigned long want_v;
45562306a36Sopenharmony_ci	unsigned long hash;
45662306a36Sopenharmony_ci	long slot;
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	hash = hpt_hash(vpn, mmu_psize_defs[psize].shift, ssize);
45962306a36Sopenharmony_ci	want_v = hpte_encode_avpn(vpn, psize, ssize);
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	/*
46262306a36Sopenharmony_ci	 * We try to keep bolted entries always in primary hash
46362306a36Sopenharmony_ci	 * But in some case we can find them in secondary too.
46462306a36Sopenharmony_ci	 */
46562306a36Sopenharmony_ci	hpte_group = (hash & htab_hash_mask) * HPTES_PER_GROUP;
46662306a36Sopenharmony_ci	slot = __native_hpte_find(want_v, hpte_group);
46762306a36Sopenharmony_ci	if (slot < 0) {
46862306a36Sopenharmony_ci		/* Try in secondary */
46962306a36Sopenharmony_ci		hpte_group = (~hash & htab_hash_mask) * HPTES_PER_GROUP;
47062306a36Sopenharmony_ci		slot = __native_hpte_find(want_v, hpte_group);
47162306a36Sopenharmony_ci		if (slot < 0)
47262306a36Sopenharmony_ci			return -1;
47362306a36Sopenharmony_ci	}
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	return slot;
47662306a36Sopenharmony_ci}
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci/*
47962306a36Sopenharmony_ci * Update the page protection bits. Intended to be used to create
48062306a36Sopenharmony_ci * guard pages for kernel data structures on pages which are bolted
48162306a36Sopenharmony_ci * in the HPT. Assumes pages being operated on will not be stolen.
48262306a36Sopenharmony_ci *
48362306a36Sopenharmony_ci * No need to lock here because we should be the only user.
48462306a36Sopenharmony_ci */
48562306a36Sopenharmony_cistatic void native_hpte_updateboltedpp(unsigned long newpp, unsigned long ea,
48662306a36Sopenharmony_ci				       int psize, int ssize)
48762306a36Sopenharmony_ci{
48862306a36Sopenharmony_ci	unsigned long vpn;
48962306a36Sopenharmony_ci	unsigned long vsid;
49062306a36Sopenharmony_ci	long slot;
49162306a36Sopenharmony_ci	struct hash_pte *hptep;
49262306a36Sopenharmony_ci	unsigned long flags;
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	local_irq_save(flags);
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	vsid = get_kernel_vsid(ea, ssize);
49762306a36Sopenharmony_ci	vpn = hpt_vpn(ea, vsid, ssize);
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	slot = native_hpte_find(vpn, psize, ssize);
50062306a36Sopenharmony_ci	if (slot == -1)
50162306a36Sopenharmony_ci		panic("could not find page to bolt\n");
50262306a36Sopenharmony_ci	hptep = htab_address + slot;
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	/* Update the HPTE */
50562306a36Sopenharmony_ci	hptep->r = cpu_to_be64((be64_to_cpu(hptep->r) &
50662306a36Sopenharmony_ci				~(HPTE_R_PPP | HPTE_R_N)) |
50762306a36Sopenharmony_ci			       (newpp & (HPTE_R_PPP | HPTE_R_N)));
50862306a36Sopenharmony_ci	/*
50962306a36Sopenharmony_ci	 * Ensure it is out of the tlb too. Bolted entries base and
51062306a36Sopenharmony_ci	 * actual page size will be same.
51162306a36Sopenharmony_ci	 */
51262306a36Sopenharmony_ci	tlbie(vpn, psize, psize, ssize, 0);
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	local_irq_restore(flags);
51562306a36Sopenharmony_ci}
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci/*
51862306a36Sopenharmony_ci * Remove a bolted kernel entry. Memory hotplug uses this.
51962306a36Sopenharmony_ci *
52062306a36Sopenharmony_ci * No need to lock here because we should be the only user.
52162306a36Sopenharmony_ci */
52262306a36Sopenharmony_cistatic int native_hpte_removebolted(unsigned long ea, int psize, int ssize)
52362306a36Sopenharmony_ci{
52462306a36Sopenharmony_ci	unsigned long vpn;
52562306a36Sopenharmony_ci	unsigned long vsid;
52662306a36Sopenharmony_ci	long slot;
52762306a36Sopenharmony_ci	struct hash_pte *hptep;
52862306a36Sopenharmony_ci	unsigned long flags;
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	local_irq_save(flags);
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	vsid = get_kernel_vsid(ea, ssize);
53362306a36Sopenharmony_ci	vpn = hpt_vpn(ea, vsid, ssize);
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	slot = native_hpte_find(vpn, psize, ssize);
53662306a36Sopenharmony_ci	if (slot == -1)
53762306a36Sopenharmony_ci		return -ENOENT;
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	hptep = htab_address + slot;
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	VM_WARN_ON(!(be64_to_cpu(hptep->v) & HPTE_V_BOLTED));
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	/* Invalidate the hpte */
54462306a36Sopenharmony_ci	hptep->v = 0;
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci	/* Invalidate the TLB */
54762306a36Sopenharmony_ci	tlbie(vpn, psize, psize, ssize, 0);
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	local_irq_restore(flags);
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci	return 0;
55262306a36Sopenharmony_ci}
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_cistatic void native_hpte_invalidate(unsigned long slot, unsigned long vpn,
55662306a36Sopenharmony_ci				   int bpsize, int apsize, int ssize, int local)
55762306a36Sopenharmony_ci{
55862306a36Sopenharmony_ci	struct hash_pte *hptep = htab_address + slot;
55962306a36Sopenharmony_ci	unsigned long hpte_v;
56062306a36Sopenharmony_ci	unsigned long want_v;
56162306a36Sopenharmony_ci	unsigned long flags;
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci	local_irq_save(flags);
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	DBG_LOW("    invalidate(vpn=%016lx, hash: %lx)\n", vpn, slot);
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	want_v = hpte_encode_avpn(vpn, bpsize, ssize);
56862306a36Sopenharmony_ci	hpte_v = hpte_get_old_v(hptep);
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	if (HPTE_V_COMPARE(hpte_v, want_v) && (hpte_v & HPTE_V_VALID)) {
57162306a36Sopenharmony_ci		native_lock_hpte(hptep);
57262306a36Sopenharmony_ci		/* recheck with locks held */
57362306a36Sopenharmony_ci		hpte_v = hpte_get_old_v(hptep);
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci		if (HPTE_V_COMPARE(hpte_v, want_v) && (hpte_v & HPTE_V_VALID)) {
57662306a36Sopenharmony_ci			/* Invalidate the hpte. NOTE: this also unlocks it */
57762306a36Sopenharmony_ci			release_hpte_lock();
57862306a36Sopenharmony_ci			hptep->v = 0;
57962306a36Sopenharmony_ci		} else
58062306a36Sopenharmony_ci			native_unlock_hpte(hptep);
58162306a36Sopenharmony_ci	}
58262306a36Sopenharmony_ci	/*
58362306a36Sopenharmony_ci	 * We need to invalidate the TLB always because hpte_remove doesn't do
58462306a36Sopenharmony_ci	 * a tlb invalidate. If a hash bucket gets full, we "evict" a more/less
58562306a36Sopenharmony_ci	 * random entry from it. When we do that we don't invalidate the TLB
58662306a36Sopenharmony_ci	 * (hpte_remove) because we assume the old translation is still
58762306a36Sopenharmony_ci	 * technically "valid".
58862306a36Sopenharmony_ci	 */
58962306a36Sopenharmony_ci	tlbie(vpn, bpsize, apsize, ssize, local);
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	local_irq_restore(flags);
59262306a36Sopenharmony_ci}
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci#ifdef CONFIG_TRANSPARENT_HUGEPAGE
59562306a36Sopenharmony_cistatic void native_hugepage_invalidate(unsigned long vsid,
59662306a36Sopenharmony_ci				       unsigned long addr,
59762306a36Sopenharmony_ci				       unsigned char *hpte_slot_array,
59862306a36Sopenharmony_ci				       int psize, int ssize, int local)
59962306a36Sopenharmony_ci{
60062306a36Sopenharmony_ci	int i;
60162306a36Sopenharmony_ci	struct hash_pte *hptep;
60262306a36Sopenharmony_ci	int actual_psize = MMU_PAGE_16M;
60362306a36Sopenharmony_ci	unsigned int max_hpte_count, valid;
60462306a36Sopenharmony_ci	unsigned long flags, s_addr = addr;
60562306a36Sopenharmony_ci	unsigned long hpte_v, want_v, shift;
60662306a36Sopenharmony_ci	unsigned long hidx, vpn = 0, hash, slot;
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci	shift = mmu_psize_defs[psize].shift;
60962306a36Sopenharmony_ci	max_hpte_count = 1U << (PMD_SHIFT - shift);
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci	local_irq_save(flags);
61262306a36Sopenharmony_ci	for (i = 0; i < max_hpte_count; i++) {
61362306a36Sopenharmony_ci		valid = hpte_valid(hpte_slot_array, i);
61462306a36Sopenharmony_ci		if (!valid)
61562306a36Sopenharmony_ci			continue;
61662306a36Sopenharmony_ci		hidx =  hpte_hash_index(hpte_slot_array, i);
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci		/* get the vpn */
61962306a36Sopenharmony_ci		addr = s_addr + (i * (1ul << shift));
62062306a36Sopenharmony_ci		vpn = hpt_vpn(addr, vsid, ssize);
62162306a36Sopenharmony_ci		hash = hpt_hash(vpn, shift, ssize);
62262306a36Sopenharmony_ci		if (hidx & _PTEIDX_SECONDARY)
62362306a36Sopenharmony_ci			hash = ~hash;
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci		slot = (hash & htab_hash_mask) * HPTES_PER_GROUP;
62662306a36Sopenharmony_ci		slot += hidx & _PTEIDX_GROUP_IX;
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci		hptep = htab_address + slot;
62962306a36Sopenharmony_ci		want_v = hpte_encode_avpn(vpn, psize, ssize);
63062306a36Sopenharmony_ci		hpte_v = hpte_get_old_v(hptep);
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci		/* Even if we miss, we need to invalidate the TLB */
63362306a36Sopenharmony_ci		if (HPTE_V_COMPARE(hpte_v, want_v) && (hpte_v & HPTE_V_VALID)) {
63462306a36Sopenharmony_ci			/* recheck with locks held */
63562306a36Sopenharmony_ci			native_lock_hpte(hptep);
63662306a36Sopenharmony_ci			hpte_v = hpte_get_old_v(hptep);
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci			if (HPTE_V_COMPARE(hpte_v, want_v) && (hpte_v & HPTE_V_VALID)) {
63962306a36Sopenharmony_ci				/* Invalidate the hpte. NOTE: this also unlocks it */
64062306a36Sopenharmony_ci				release_hpte_lock();
64162306a36Sopenharmony_ci				hptep->v = 0;
64262306a36Sopenharmony_ci			} else
64362306a36Sopenharmony_ci				native_unlock_hpte(hptep);
64462306a36Sopenharmony_ci		}
64562306a36Sopenharmony_ci		/*
64662306a36Sopenharmony_ci		 * We need to do tlb invalidate for all the address, tlbie
64762306a36Sopenharmony_ci		 * instruction compares entry_VA in tlb with the VA specified
64862306a36Sopenharmony_ci		 * here
64962306a36Sopenharmony_ci		 */
65062306a36Sopenharmony_ci		tlbie(vpn, psize, actual_psize, ssize, local);
65162306a36Sopenharmony_ci	}
65262306a36Sopenharmony_ci	local_irq_restore(flags);
65362306a36Sopenharmony_ci}
65462306a36Sopenharmony_ci#else
65562306a36Sopenharmony_cistatic void native_hugepage_invalidate(unsigned long vsid,
65662306a36Sopenharmony_ci				       unsigned long addr,
65762306a36Sopenharmony_ci				       unsigned char *hpte_slot_array,
65862306a36Sopenharmony_ci				       int psize, int ssize, int local)
65962306a36Sopenharmony_ci{
66062306a36Sopenharmony_ci	WARN(1, "%s called without THP support\n", __func__);
66162306a36Sopenharmony_ci}
66262306a36Sopenharmony_ci#endif
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_cistatic void hpte_decode(struct hash_pte *hpte, unsigned long slot,
66562306a36Sopenharmony_ci			int *psize, int *apsize, int *ssize, unsigned long *vpn)
66662306a36Sopenharmony_ci{
66762306a36Sopenharmony_ci	unsigned long avpn, pteg, vpi;
66862306a36Sopenharmony_ci	unsigned long hpte_v = be64_to_cpu(hpte->v);
66962306a36Sopenharmony_ci	unsigned long hpte_r = be64_to_cpu(hpte->r);
67062306a36Sopenharmony_ci	unsigned long vsid, seg_off;
67162306a36Sopenharmony_ci	int size, a_size, shift;
67262306a36Sopenharmony_ci	/* Look at the 8 bit LP value */
67362306a36Sopenharmony_ci	unsigned int lp = (hpte_r >> LP_SHIFT) & ((1 << LP_BITS) - 1);
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci	if (cpu_has_feature(CPU_FTR_ARCH_300)) {
67662306a36Sopenharmony_ci		hpte_v = hpte_new_to_old_v(hpte_v, hpte_r);
67762306a36Sopenharmony_ci		hpte_r = hpte_new_to_old_r(hpte_r);
67862306a36Sopenharmony_ci	}
67962306a36Sopenharmony_ci	if (!(hpte_v & HPTE_V_LARGE)) {
68062306a36Sopenharmony_ci		size   = MMU_PAGE_4K;
68162306a36Sopenharmony_ci		a_size = MMU_PAGE_4K;
68262306a36Sopenharmony_ci	} else {
68362306a36Sopenharmony_ci		size = hpte_page_sizes[lp] & 0xf;
68462306a36Sopenharmony_ci		a_size = hpte_page_sizes[lp] >> 4;
68562306a36Sopenharmony_ci	}
68662306a36Sopenharmony_ci	/* This works for all page sizes, and for 256M and 1T segments */
68762306a36Sopenharmony_ci	*ssize = hpte_v >> HPTE_V_SSIZE_SHIFT;
68862306a36Sopenharmony_ci	shift = mmu_psize_defs[size].shift;
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	avpn = (HPTE_V_AVPN_VAL(hpte_v) & ~mmu_psize_defs[size].avpnm);
69162306a36Sopenharmony_ci	pteg = slot / HPTES_PER_GROUP;
69262306a36Sopenharmony_ci	if (hpte_v & HPTE_V_SECONDARY)
69362306a36Sopenharmony_ci		pteg = ~pteg;
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci	switch (*ssize) {
69662306a36Sopenharmony_ci	case MMU_SEGSIZE_256M:
69762306a36Sopenharmony_ci		/* We only have 28 - 23 bits of seg_off in avpn */
69862306a36Sopenharmony_ci		seg_off = (avpn & 0x1f) << 23;
69962306a36Sopenharmony_ci		vsid    =  avpn >> 5;
70062306a36Sopenharmony_ci		/* We can find more bits from the pteg value */
70162306a36Sopenharmony_ci		if (shift < 23) {
70262306a36Sopenharmony_ci			vpi = (vsid ^ pteg) & htab_hash_mask;
70362306a36Sopenharmony_ci			seg_off |= vpi << shift;
70462306a36Sopenharmony_ci		}
70562306a36Sopenharmony_ci		*vpn = vsid << (SID_SHIFT - VPN_SHIFT) | seg_off >> VPN_SHIFT;
70662306a36Sopenharmony_ci		break;
70762306a36Sopenharmony_ci	case MMU_SEGSIZE_1T:
70862306a36Sopenharmony_ci		/* We only have 40 - 23 bits of seg_off in avpn */
70962306a36Sopenharmony_ci		seg_off = (avpn & 0x1ffff) << 23;
71062306a36Sopenharmony_ci		vsid    = avpn >> 17;
71162306a36Sopenharmony_ci		if (shift < 23) {
71262306a36Sopenharmony_ci			vpi = (vsid ^ (vsid << 25) ^ pteg) & htab_hash_mask;
71362306a36Sopenharmony_ci			seg_off |= vpi << shift;
71462306a36Sopenharmony_ci		}
71562306a36Sopenharmony_ci		*vpn = vsid << (SID_SHIFT_1T - VPN_SHIFT) | seg_off >> VPN_SHIFT;
71662306a36Sopenharmony_ci		break;
71762306a36Sopenharmony_ci	default:
71862306a36Sopenharmony_ci		*vpn = size = 0;
71962306a36Sopenharmony_ci	}
72062306a36Sopenharmony_ci	*psize  = size;
72162306a36Sopenharmony_ci	*apsize = a_size;
72262306a36Sopenharmony_ci}
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci/*
72562306a36Sopenharmony_ci * clear all mappings on kexec.  All cpus are in real mode (or they will
72662306a36Sopenharmony_ci * be when they isi), and we are the only one left.  We rely on our kernel
72762306a36Sopenharmony_ci * mapping being 0xC0's and the hardware ignoring those two real bits.
72862306a36Sopenharmony_ci *
72962306a36Sopenharmony_ci * This must be called with interrupts disabled.
73062306a36Sopenharmony_ci *
73162306a36Sopenharmony_ci * Taking the native_tlbie_lock is unsafe here due to the possibility of
73262306a36Sopenharmony_ci * lockdep being on. On pre POWER5 hardware, not taking the lock could
73362306a36Sopenharmony_ci * cause deadlock. POWER5 and newer not taking the lock is fine. This only
73462306a36Sopenharmony_ci * gets called during boot before secondary CPUs have come up and during
73562306a36Sopenharmony_ci * crashdump and all bets are off anyway.
73662306a36Sopenharmony_ci *
73762306a36Sopenharmony_ci * TODO: add batching support when enabled.  remember, no dynamic memory here,
73862306a36Sopenharmony_ci * although there is the control page available...
73962306a36Sopenharmony_ci */
74062306a36Sopenharmony_cistatic notrace void native_hpte_clear(void)
74162306a36Sopenharmony_ci{
74262306a36Sopenharmony_ci	unsigned long vpn = 0;
74362306a36Sopenharmony_ci	unsigned long slot, slots;
74462306a36Sopenharmony_ci	struct hash_pte *hptep = htab_address;
74562306a36Sopenharmony_ci	unsigned long hpte_v;
74662306a36Sopenharmony_ci	unsigned long pteg_count;
74762306a36Sopenharmony_ci	int psize, apsize, ssize;
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci	pteg_count = htab_hash_mask + 1;
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ci	slots = pteg_count * HPTES_PER_GROUP;
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_ci	for (slot = 0; slot < slots; slot++, hptep++) {
75462306a36Sopenharmony_ci		/*
75562306a36Sopenharmony_ci		 * we could lock the pte here, but we are the only cpu
75662306a36Sopenharmony_ci		 * running,  right?  and for crash dump, we probably
75762306a36Sopenharmony_ci		 * don't want to wait for a maybe bad cpu.
75862306a36Sopenharmony_ci		 */
75962306a36Sopenharmony_ci		hpte_v = be64_to_cpu(hptep->v);
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci		/*
76262306a36Sopenharmony_ci		 * Call __tlbie() here rather than tlbie() since we can't take the
76362306a36Sopenharmony_ci		 * native_tlbie_lock.
76462306a36Sopenharmony_ci		 */
76562306a36Sopenharmony_ci		if (hpte_v & HPTE_V_VALID) {
76662306a36Sopenharmony_ci			hpte_decode(hptep, slot, &psize, &apsize, &ssize, &vpn);
76762306a36Sopenharmony_ci			hptep->v = 0;
76862306a36Sopenharmony_ci			___tlbie(vpn, psize, apsize, ssize);
76962306a36Sopenharmony_ci		}
77062306a36Sopenharmony_ci	}
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci	asm volatile("eieio; tlbsync; ptesync":::"memory");
77362306a36Sopenharmony_ci}
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci/*
77662306a36Sopenharmony_ci * Batched hash table flush, we batch the tlbie's to avoid taking/releasing
77762306a36Sopenharmony_ci * the lock all the time
77862306a36Sopenharmony_ci */
77962306a36Sopenharmony_cistatic void native_flush_hash_range(unsigned long number, int local)
78062306a36Sopenharmony_ci{
78162306a36Sopenharmony_ci	unsigned long vpn = 0;
78262306a36Sopenharmony_ci	unsigned long hash, index, hidx, shift, slot;
78362306a36Sopenharmony_ci	struct hash_pte *hptep;
78462306a36Sopenharmony_ci	unsigned long hpte_v;
78562306a36Sopenharmony_ci	unsigned long want_v;
78662306a36Sopenharmony_ci	unsigned long flags;
78762306a36Sopenharmony_ci	real_pte_t pte;
78862306a36Sopenharmony_ci	struct ppc64_tlb_batch *batch = this_cpu_ptr(&ppc64_tlb_batch);
78962306a36Sopenharmony_ci	unsigned long psize = batch->psize;
79062306a36Sopenharmony_ci	int ssize = batch->ssize;
79162306a36Sopenharmony_ci	int i;
79262306a36Sopenharmony_ci	unsigned int use_local;
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci	use_local = local && mmu_has_feature(MMU_FTR_TLBIEL) &&
79562306a36Sopenharmony_ci		mmu_psize_defs[psize].tlbiel && !cxl_ctx_in_use();
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ci	local_irq_save(flags);
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_ci	for (i = 0; i < number; i++) {
80062306a36Sopenharmony_ci		vpn = batch->vpn[i];
80162306a36Sopenharmony_ci		pte = batch->pte[i];
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_ci		pte_iterate_hashed_subpages(pte, psize, vpn, index, shift) {
80462306a36Sopenharmony_ci			hash = hpt_hash(vpn, shift, ssize);
80562306a36Sopenharmony_ci			hidx = __rpte_to_hidx(pte, index);
80662306a36Sopenharmony_ci			if (hidx & _PTEIDX_SECONDARY)
80762306a36Sopenharmony_ci				hash = ~hash;
80862306a36Sopenharmony_ci			slot = (hash & htab_hash_mask) * HPTES_PER_GROUP;
80962306a36Sopenharmony_ci			slot += hidx & _PTEIDX_GROUP_IX;
81062306a36Sopenharmony_ci			hptep = htab_address + slot;
81162306a36Sopenharmony_ci			want_v = hpte_encode_avpn(vpn, psize, ssize);
81262306a36Sopenharmony_ci			hpte_v = hpte_get_old_v(hptep);
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci			if (!HPTE_V_COMPARE(hpte_v, want_v) || !(hpte_v & HPTE_V_VALID))
81562306a36Sopenharmony_ci				continue;
81662306a36Sopenharmony_ci			/* lock and try again */
81762306a36Sopenharmony_ci			native_lock_hpte(hptep);
81862306a36Sopenharmony_ci			hpte_v = hpte_get_old_v(hptep);
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci			if (!HPTE_V_COMPARE(hpte_v, want_v) || !(hpte_v & HPTE_V_VALID))
82162306a36Sopenharmony_ci				native_unlock_hpte(hptep);
82262306a36Sopenharmony_ci			else {
82362306a36Sopenharmony_ci				release_hpte_lock();
82462306a36Sopenharmony_ci				hptep->v = 0;
82562306a36Sopenharmony_ci			}
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci		} pte_iterate_hashed_end();
82862306a36Sopenharmony_ci	}
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci	if (use_local) {
83162306a36Sopenharmony_ci		asm volatile("ptesync":::"memory");
83262306a36Sopenharmony_ci		for (i = 0; i < number; i++) {
83362306a36Sopenharmony_ci			vpn = batch->vpn[i];
83462306a36Sopenharmony_ci			pte = batch->pte[i];
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci			pte_iterate_hashed_subpages(pte, psize,
83762306a36Sopenharmony_ci						    vpn, index, shift) {
83862306a36Sopenharmony_ci				__tlbiel(vpn, psize, psize, ssize);
83962306a36Sopenharmony_ci			} pte_iterate_hashed_end();
84062306a36Sopenharmony_ci		}
84162306a36Sopenharmony_ci		ppc_after_tlbiel_barrier();
84262306a36Sopenharmony_ci	} else {
84362306a36Sopenharmony_ci		int lock_tlbie = !mmu_has_feature(MMU_FTR_LOCKLESS_TLBIE);
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_ci		if (lock_tlbie)
84662306a36Sopenharmony_ci			raw_spin_lock(&native_tlbie_lock);
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci		asm volatile("ptesync":::"memory");
84962306a36Sopenharmony_ci		for (i = 0; i < number; i++) {
85062306a36Sopenharmony_ci			vpn = batch->vpn[i];
85162306a36Sopenharmony_ci			pte = batch->pte[i];
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_ci			pte_iterate_hashed_subpages(pte, psize,
85462306a36Sopenharmony_ci						    vpn, index, shift) {
85562306a36Sopenharmony_ci				__tlbie(vpn, psize, psize, ssize);
85662306a36Sopenharmony_ci			} pte_iterate_hashed_end();
85762306a36Sopenharmony_ci		}
85862306a36Sopenharmony_ci		/*
85962306a36Sopenharmony_ci		 * Just do one more with the last used values.
86062306a36Sopenharmony_ci		 */
86162306a36Sopenharmony_ci		fixup_tlbie_vpn(vpn, psize, psize, ssize);
86262306a36Sopenharmony_ci		asm volatile("eieio; tlbsync; ptesync":::"memory");
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci		if (lock_tlbie)
86562306a36Sopenharmony_ci			raw_spin_unlock(&native_tlbie_lock);
86662306a36Sopenharmony_ci	}
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci	local_irq_restore(flags);
86962306a36Sopenharmony_ci}
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_civoid __init hpte_init_native(void)
87262306a36Sopenharmony_ci{
87362306a36Sopenharmony_ci	mmu_hash_ops.hpte_invalidate	= native_hpte_invalidate;
87462306a36Sopenharmony_ci	mmu_hash_ops.hpte_updatepp	= native_hpte_updatepp;
87562306a36Sopenharmony_ci	mmu_hash_ops.hpte_updateboltedpp = native_hpte_updateboltedpp;
87662306a36Sopenharmony_ci	mmu_hash_ops.hpte_removebolted = native_hpte_removebolted;
87762306a36Sopenharmony_ci	mmu_hash_ops.hpte_insert	= native_hpte_insert;
87862306a36Sopenharmony_ci	mmu_hash_ops.hpte_remove	= native_hpte_remove;
87962306a36Sopenharmony_ci	mmu_hash_ops.hpte_clear_all	= native_hpte_clear;
88062306a36Sopenharmony_ci	mmu_hash_ops.flush_hash_range = native_flush_hash_range;
88162306a36Sopenharmony_ci	mmu_hash_ops.hugepage_invalidate   = native_hugepage_invalidate;
88262306a36Sopenharmony_ci}
883