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