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