162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * TLB flush routines for radix kernels. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright 2015-2016, Aneesh Kumar K.V, IBM Corporation. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/mm.h> 962306a36Sopenharmony_ci#include <linux/hugetlb.h> 1062306a36Sopenharmony_ci#include <linux/memblock.h> 1162306a36Sopenharmony_ci#include <linux/mmu_context.h> 1262306a36Sopenharmony_ci#include <linux/sched/mm.h> 1362306a36Sopenharmony_ci#include <linux/debugfs.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <asm/ppc-opcode.h> 1662306a36Sopenharmony_ci#include <asm/tlb.h> 1762306a36Sopenharmony_ci#include <asm/tlbflush.h> 1862306a36Sopenharmony_ci#include <asm/trace.h> 1962306a36Sopenharmony_ci#include <asm/cputhreads.h> 2062306a36Sopenharmony_ci#include <asm/plpar_wrappers.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include "internal.h" 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci/* 2562306a36Sopenharmony_ci * tlbiel instruction for radix, set invalidation 2662306a36Sopenharmony_ci * i.e., r=1 and is=01 or is=10 or is=11 2762306a36Sopenharmony_ci */ 2862306a36Sopenharmony_cistatic __always_inline void tlbiel_radix_set_isa300(unsigned int set, unsigned int is, 2962306a36Sopenharmony_ci unsigned int pid, 3062306a36Sopenharmony_ci unsigned int ric, unsigned int prs) 3162306a36Sopenharmony_ci{ 3262306a36Sopenharmony_ci unsigned long rb; 3362306a36Sopenharmony_ci unsigned long rs; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci rb = (set << PPC_BITLSHIFT(51)) | (is << PPC_BITLSHIFT(53)); 3662306a36Sopenharmony_ci rs = ((unsigned long)pid << PPC_BITLSHIFT(31)); 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci asm volatile(PPC_TLBIEL(%0, %1, %2, %3, 1) 3962306a36Sopenharmony_ci : : "r"(rb), "r"(rs), "i"(ric), "i"(prs) 4062306a36Sopenharmony_ci : "memory"); 4162306a36Sopenharmony_ci} 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic void tlbiel_all_isa300(unsigned int num_sets, unsigned int is) 4462306a36Sopenharmony_ci{ 4562306a36Sopenharmony_ci unsigned int set; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci asm volatile("ptesync": : :"memory"); 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci /* 5062306a36Sopenharmony_ci * Flush the first set of the TLB, and the entire Page Walk Cache 5162306a36Sopenharmony_ci * and partition table entries. Then flush the remaining sets of the 5262306a36Sopenharmony_ci * TLB. 5362306a36Sopenharmony_ci */ 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci if (early_cpu_has_feature(CPU_FTR_HVMODE)) { 5662306a36Sopenharmony_ci /* MSR[HV] should flush partition scope translations first. */ 5762306a36Sopenharmony_ci tlbiel_radix_set_isa300(0, is, 0, RIC_FLUSH_ALL, 0); 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci if (!early_cpu_has_feature(CPU_FTR_ARCH_31)) { 6062306a36Sopenharmony_ci for (set = 1; set < num_sets; set++) 6162306a36Sopenharmony_ci tlbiel_radix_set_isa300(set, is, 0, 6262306a36Sopenharmony_ci RIC_FLUSH_TLB, 0); 6362306a36Sopenharmony_ci } 6462306a36Sopenharmony_ci } 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci /* Flush process scoped entries. */ 6762306a36Sopenharmony_ci tlbiel_radix_set_isa300(0, is, 0, RIC_FLUSH_ALL, 1); 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci if (!early_cpu_has_feature(CPU_FTR_ARCH_31)) { 7062306a36Sopenharmony_ci for (set = 1; set < num_sets; set++) 7162306a36Sopenharmony_ci tlbiel_radix_set_isa300(set, is, 0, RIC_FLUSH_TLB, 1); 7262306a36Sopenharmony_ci } 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci ppc_after_tlbiel_barrier(); 7562306a36Sopenharmony_ci} 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_civoid radix__tlbiel_all(unsigned int action) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci unsigned int is; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci switch (action) { 8262306a36Sopenharmony_ci case TLB_INVAL_SCOPE_GLOBAL: 8362306a36Sopenharmony_ci is = 3; 8462306a36Sopenharmony_ci break; 8562306a36Sopenharmony_ci case TLB_INVAL_SCOPE_LPID: 8662306a36Sopenharmony_ci is = 2; 8762306a36Sopenharmony_ci break; 8862306a36Sopenharmony_ci default: 8962306a36Sopenharmony_ci BUG(); 9062306a36Sopenharmony_ci } 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci if (early_cpu_has_feature(CPU_FTR_ARCH_300)) 9362306a36Sopenharmony_ci tlbiel_all_isa300(POWER9_TLB_SETS_RADIX, is); 9462306a36Sopenharmony_ci else 9562306a36Sopenharmony_ci WARN(1, "%s called on pre-POWER9 CPU\n", __func__); 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci asm volatile(PPC_ISA_3_0_INVALIDATE_ERAT "; isync" : : :"memory"); 9862306a36Sopenharmony_ci} 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistatic __always_inline void __tlbiel_pid(unsigned long pid, int set, 10162306a36Sopenharmony_ci unsigned long ric) 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci unsigned long rb,rs,prs,r; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci rb = PPC_BIT(53); /* IS = 1 */ 10662306a36Sopenharmony_ci rb |= set << PPC_BITLSHIFT(51); 10762306a36Sopenharmony_ci rs = ((unsigned long)pid) << PPC_BITLSHIFT(31); 10862306a36Sopenharmony_ci prs = 1; /* process scoped */ 10962306a36Sopenharmony_ci r = 1; /* radix format */ 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci asm volatile(PPC_TLBIEL(%0, %4, %3, %2, %1) 11262306a36Sopenharmony_ci : : "r"(rb), "i"(r), "i"(prs), "i"(ric), "r"(rs) : "memory"); 11362306a36Sopenharmony_ci trace_tlbie(0, 1, rb, rs, ric, prs, r); 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistatic __always_inline void __tlbie_pid(unsigned long pid, unsigned long ric) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci unsigned long rb,rs,prs,r; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci rb = PPC_BIT(53); /* IS = 1 */ 12162306a36Sopenharmony_ci rs = pid << PPC_BITLSHIFT(31); 12262306a36Sopenharmony_ci prs = 1; /* process scoped */ 12362306a36Sopenharmony_ci r = 1; /* radix format */ 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci asm volatile(PPC_TLBIE_5(%0, %4, %3, %2, %1) 12662306a36Sopenharmony_ci : : "r"(rb), "i"(r), "i"(prs), "i"(ric), "r"(rs) : "memory"); 12762306a36Sopenharmony_ci trace_tlbie(0, 0, rb, rs, ric, prs, r); 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistatic __always_inline void __tlbie_lpid(unsigned long lpid, unsigned long ric) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci unsigned long rb,rs,prs,r; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci rb = PPC_BIT(52); /* IS = 2 */ 13562306a36Sopenharmony_ci rs = lpid; 13662306a36Sopenharmony_ci prs = 0; /* partition scoped */ 13762306a36Sopenharmony_ci r = 1; /* radix format */ 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci asm volatile(PPC_TLBIE_5(%0, %4, %3, %2, %1) 14062306a36Sopenharmony_ci : : "r"(rb), "i"(r), "i"(prs), "i"(ric), "r"(rs) : "memory"); 14162306a36Sopenharmony_ci trace_tlbie(lpid, 0, rb, rs, ric, prs, r); 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic __always_inline void __tlbie_lpid_guest(unsigned long lpid, unsigned long ric) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci unsigned long rb,rs,prs,r; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci rb = PPC_BIT(52); /* IS = 2 */ 14962306a36Sopenharmony_ci rs = lpid; 15062306a36Sopenharmony_ci prs = 1; /* process scoped */ 15162306a36Sopenharmony_ci r = 1; /* radix format */ 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci asm volatile(PPC_TLBIE_5(%0, %4, %3, %2, %1) 15462306a36Sopenharmony_ci : : "r"(rb), "i"(r), "i"(prs), "i"(ric), "r"(rs) : "memory"); 15562306a36Sopenharmony_ci trace_tlbie(lpid, 0, rb, rs, ric, prs, r); 15662306a36Sopenharmony_ci} 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_cistatic __always_inline void __tlbiel_va(unsigned long va, unsigned long pid, 15962306a36Sopenharmony_ci unsigned long ap, unsigned long ric) 16062306a36Sopenharmony_ci{ 16162306a36Sopenharmony_ci unsigned long rb,rs,prs,r; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci rb = va & ~(PPC_BITMASK(52, 63)); 16462306a36Sopenharmony_ci rb |= ap << PPC_BITLSHIFT(58); 16562306a36Sopenharmony_ci rs = pid << PPC_BITLSHIFT(31); 16662306a36Sopenharmony_ci prs = 1; /* process scoped */ 16762306a36Sopenharmony_ci r = 1; /* radix format */ 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci asm volatile(PPC_TLBIEL(%0, %4, %3, %2, %1) 17062306a36Sopenharmony_ci : : "r"(rb), "i"(r), "i"(prs), "i"(ric), "r"(rs) : "memory"); 17162306a36Sopenharmony_ci trace_tlbie(0, 1, rb, rs, ric, prs, r); 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_cistatic __always_inline void __tlbie_va(unsigned long va, unsigned long pid, 17562306a36Sopenharmony_ci unsigned long ap, unsigned long ric) 17662306a36Sopenharmony_ci{ 17762306a36Sopenharmony_ci unsigned long rb,rs,prs,r; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci rb = va & ~(PPC_BITMASK(52, 63)); 18062306a36Sopenharmony_ci rb |= ap << PPC_BITLSHIFT(58); 18162306a36Sopenharmony_ci rs = pid << PPC_BITLSHIFT(31); 18262306a36Sopenharmony_ci prs = 1; /* process scoped */ 18362306a36Sopenharmony_ci r = 1; /* radix format */ 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci asm volatile(PPC_TLBIE_5(%0, %4, %3, %2, %1) 18662306a36Sopenharmony_ci : : "r"(rb), "i"(r), "i"(prs), "i"(ric), "r"(rs) : "memory"); 18762306a36Sopenharmony_ci trace_tlbie(0, 0, rb, rs, ric, prs, r); 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_cistatic __always_inline void __tlbie_lpid_va(unsigned long va, unsigned long lpid, 19162306a36Sopenharmony_ci unsigned long ap, unsigned long ric) 19262306a36Sopenharmony_ci{ 19362306a36Sopenharmony_ci unsigned long rb,rs,prs,r; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci rb = va & ~(PPC_BITMASK(52, 63)); 19662306a36Sopenharmony_ci rb |= ap << PPC_BITLSHIFT(58); 19762306a36Sopenharmony_ci rs = lpid; 19862306a36Sopenharmony_ci prs = 0; /* partition scoped */ 19962306a36Sopenharmony_ci r = 1; /* radix format */ 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci asm volatile(PPC_TLBIE_5(%0, %4, %3, %2, %1) 20262306a36Sopenharmony_ci : : "r"(rb), "i"(r), "i"(prs), "i"(ric), "r"(rs) : "memory"); 20362306a36Sopenharmony_ci trace_tlbie(lpid, 0, rb, rs, ric, prs, r); 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_cistatic inline void fixup_tlbie_va(unsigned long va, unsigned long pid, 20862306a36Sopenharmony_ci unsigned long ap) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci if (cpu_has_feature(CPU_FTR_P9_TLBIE_ERAT_BUG)) { 21162306a36Sopenharmony_ci asm volatile("ptesync": : :"memory"); 21262306a36Sopenharmony_ci __tlbie_va(va, 0, ap, RIC_FLUSH_TLB); 21362306a36Sopenharmony_ci } 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci if (cpu_has_feature(CPU_FTR_P9_TLBIE_STQ_BUG)) { 21662306a36Sopenharmony_ci asm volatile("ptesync": : :"memory"); 21762306a36Sopenharmony_ci __tlbie_va(va, pid, ap, RIC_FLUSH_TLB); 21862306a36Sopenharmony_ci } 21962306a36Sopenharmony_ci} 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_cistatic inline void fixup_tlbie_va_range(unsigned long va, unsigned long pid, 22262306a36Sopenharmony_ci unsigned long ap) 22362306a36Sopenharmony_ci{ 22462306a36Sopenharmony_ci if (cpu_has_feature(CPU_FTR_P9_TLBIE_ERAT_BUG)) { 22562306a36Sopenharmony_ci asm volatile("ptesync": : :"memory"); 22662306a36Sopenharmony_ci __tlbie_pid(0, RIC_FLUSH_TLB); 22762306a36Sopenharmony_ci } 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci if (cpu_has_feature(CPU_FTR_P9_TLBIE_STQ_BUG)) { 23062306a36Sopenharmony_ci asm volatile("ptesync": : :"memory"); 23162306a36Sopenharmony_ci __tlbie_va(va, pid, ap, RIC_FLUSH_TLB); 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci} 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_cistatic inline void fixup_tlbie_pid(unsigned long pid) 23662306a36Sopenharmony_ci{ 23762306a36Sopenharmony_ci /* 23862306a36Sopenharmony_ci * We can use any address for the invalidation, pick one which is 23962306a36Sopenharmony_ci * probably unused as an optimisation. 24062306a36Sopenharmony_ci */ 24162306a36Sopenharmony_ci unsigned long va = ((1UL << 52) - 1); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci if (cpu_has_feature(CPU_FTR_P9_TLBIE_ERAT_BUG)) { 24462306a36Sopenharmony_ci asm volatile("ptesync": : :"memory"); 24562306a36Sopenharmony_ci __tlbie_pid(0, RIC_FLUSH_TLB); 24662306a36Sopenharmony_ci } 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci if (cpu_has_feature(CPU_FTR_P9_TLBIE_STQ_BUG)) { 24962306a36Sopenharmony_ci asm volatile("ptesync": : :"memory"); 25062306a36Sopenharmony_ci __tlbie_va(va, pid, mmu_get_ap(MMU_PAGE_64K), RIC_FLUSH_TLB); 25162306a36Sopenharmony_ci } 25262306a36Sopenharmony_ci} 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_cistatic inline void fixup_tlbie_lpid_va(unsigned long va, unsigned long lpid, 25562306a36Sopenharmony_ci unsigned long ap) 25662306a36Sopenharmony_ci{ 25762306a36Sopenharmony_ci if (cpu_has_feature(CPU_FTR_P9_TLBIE_ERAT_BUG)) { 25862306a36Sopenharmony_ci asm volatile("ptesync": : :"memory"); 25962306a36Sopenharmony_ci __tlbie_lpid_va(va, 0, ap, RIC_FLUSH_TLB); 26062306a36Sopenharmony_ci } 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci if (cpu_has_feature(CPU_FTR_P9_TLBIE_STQ_BUG)) { 26362306a36Sopenharmony_ci asm volatile("ptesync": : :"memory"); 26462306a36Sopenharmony_ci __tlbie_lpid_va(va, lpid, ap, RIC_FLUSH_TLB); 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci} 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_cistatic inline void fixup_tlbie_lpid(unsigned long lpid) 26962306a36Sopenharmony_ci{ 27062306a36Sopenharmony_ci /* 27162306a36Sopenharmony_ci * We can use any address for the invalidation, pick one which is 27262306a36Sopenharmony_ci * probably unused as an optimisation. 27362306a36Sopenharmony_ci */ 27462306a36Sopenharmony_ci unsigned long va = ((1UL << 52) - 1); 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci if (cpu_has_feature(CPU_FTR_P9_TLBIE_ERAT_BUG)) { 27762306a36Sopenharmony_ci asm volatile("ptesync": : :"memory"); 27862306a36Sopenharmony_ci __tlbie_lpid(0, RIC_FLUSH_TLB); 27962306a36Sopenharmony_ci } 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci if (cpu_has_feature(CPU_FTR_P9_TLBIE_STQ_BUG)) { 28262306a36Sopenharmony_ci asm volatile("ptesync": : :"memory"); 28362306a36Sopenharmony_ci __tlbie_lpid_va(va, lpid, mmu_get_ap(MMU_PAGE_64K), RIC_FLUSH_TLB); 28462306a36Sopenharmony_ci } 28562306a36Sopenharmony_ci} 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci/* 28862306a36Sopenharmony_ci * We use 128 set in radix mode and 256 set in hpt mode. 28962306a36Sopenharmony_ci */ 29062306a36Sopenharmony_cistatic inline void _tlbiel_pid(unsigned long pid, unsigned long ric) 29162306a36Sopenharmony_ci{ 29262306a36Sopenharmony_ci int set; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci asm volatile("ptesync": : :"memory"); 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci switch (ric) { 29762306a36Sopenharmony_ci case RIC_FLUSH_PWC: 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci /* For PWC, only one flush is needed */ 30062306a36Sopenharmony_ci __tlbiel_pid(pid, 0, RIC_FLUSH_PWC); 30162306a36Sopenharmony_ci ppc_after_tlbiel_barrier(); 30262306a36Sopenharmony_ci return; 30362306a36Sopenharmony_ci case RIC_FLUSH_TLB: 30462306a36Sopenharmony_ci __tlbiel_pid(pid, 0, RIC_FLUSH_TLB); 30562306a36Sopenharmony_ci break; 30662306a36Sopenharmony_ci case RIC_FLUSH_ALL: 30762306a36Sopenharmony_ci default: 30862306a36Sopenharmony_ci /* 30962306a36Sopenharmony_ci * Flush the first set of the TLB, and if 31062306a36Sopenharmony_ci * we're doing a RIC_FLUSH_ALL, also flush 31162306a36Sopenharmony_ci * the entire Page Walk Cache. 31262306a36Sopenharmony_ci */ 31362306a36Sopenharmony_ci __tlbiel_pid(pid, 0, RIC_FLUSH_ALL); 31462306a36Sopenharmony_ci } 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci if (!cpu_has_feature(CPU_FTR_ARCH_31)) { 31762306a36Sopenharmony_ci /* For the remaining sets, just flush the TLB */ 31862306a36Sopenharmony_ci for (set = 1; set < POWER9_TLB_SETS_RADIX ; set++) 31962306a36Sopenharmony_ci __tlbiel_pid(pid, set, RIC_FLUSH_TLB); 32062306a36Sopenharmony_ci } 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci ppc_after_tlbiel_barrier(); 32362306a36Sopenharmony_ci asm volatile(PPC_RADIX_INVALIDATE_ERAT_USER "; isync" : : :"memory"); 32462306a36Sopenharmony_ci} 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_cistatic inline void _tlbie_pid(unsigned long pid, unsigned long ric) 32762306a36Sopenharmony_ci{ 32862306a36Sopenharmony_ci asm volatile("ptesync": : :"memory"); 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci /* 33162306a36Sopenharmony_ci * Workaround the fact that the "ric" argument to __tlbie_pid 33262306a36Sopenharmony_ci * must be a compile-time constraint to match the "i" constraint 33362306a36Sopenharmony_ci * in the asm statement. 33462306a36Sopenharmony_ci */ 33562306a36Sopenharmony_ci switch (ric) { 33662306a36Sopenharmony_ci case RIC_FLUSH_TLB: 33762306a36Sopenharmony_ci __tlbie_pid(pid, RIC_FLUSH_TLB); 33862306a36Sopenharmony_ci fixup_tlbie_pid(pid); 33962306a36Sopenharmony_ci break; 34062306a36Sopenharmony_ci case RIC_FLUSH_PWC: 34162306a36Sopenharmony_ci __tlbie_pid(pid, RIC_FLUSH_PWC); 34262306a36Sopenharmony_ci break; 34362306a36Sopenharmony_ci case RIC_FLUSH_ALL: 34462306a36Sopenharmony_ci default: 34562306a36Sopenharmony_ci __tlbie_pid(pid, RIC_FLUSH_ALL); 34662306a36Sopenharmony_ci fixup_tlbie_pid(pid); 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci asm volatile("eieio; tlbsync; ptesync": : :"memory"); 34962306a36Sopenharmony_ci} 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_cistruct tlbiel_pid { 35262306a36Sopenharmony_ci unsigned long pid; 35362306a36Sopenharmony_ci unsigned long ric; 35462306a36Sopenharmony_ci}; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_cistatic void do_tlbiel_pid(void *info) 35762306a36Sopenharmony_ci{ 35862306a36Sopenharmony_ci struct tlbiel_pid *t = info; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci if (t->ric == RIC_FLUSH_TLB) 36162306a36Sopenharmony_ci _tlbiel_pid(t->pid, RIC_FLUSH_TLB); 36262306a36Sopenharmony_ci else if (t->ric == RIC_FLUSH_PWC) 36362306a36Sopenharmony_ci _tlbiel_pid(t->pid, RIC_FLUSH_PWC); 36462306a36Sopenharmony_ci else 36562306a36Sopenharmony_ci _tlbiel_pid(t->pid, RIC_FLUSH_ALL); 36662306a36Sopenharmony_ci} 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_cistatic inline void _tlbiel_pid_multicast(struct mm_struct *mm, 36962306a36Sopenharmony_ci unsigned long pid, unsigned long ric) 37062306a36Sopenharmony_ci{ 37162306a36Sopenharmony_ci struct cpumask *cpus = mm_cpumask(mm); 37262306a36Sopenharmony_ci struct tlbiel_pid t = { .pid = pid, .ric = ric }; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci on_each_cpu_mask(cpus, do_tlbiel_pid, &t, 1); 37562306a36Sopenharmony_ci /* 37662306a36Sopenharmony_ci * Always want the CPU translations to be invalidated with tlbiel in 37762306a36Sopenharmony_ci * these paths, so while coprocessors must use tlbie, we can not 37862306a36Sopenharmony_ci * optimise away the tlbiel component. 37962306a36Sopenharmony_ci */ 38062306a36Sopenharmony_ci if (atomic_read(&mm->context.copros) > 0) 38162306a36Sopenharmony_ci _tlbie_pid(pid, RIC_FLUSH_ALL); 38262306a36Sopenharmony_ci} 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_cistatic inline void _tlbie_lpid(unsigned long lpid, unsigned long ric) 38562306a36Sopenharmony_ci{ 38662306a36Sopenharmony_ci asm volatile("ptesync": : :"memory"); 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci /* 38962306a36Sopenharmony_ci * Workaround the fact that the "ric" argument to __tlbie_pid 39062306a36Sopenharmony_ci * must be a compile-time contraint to match the "i" constraint 39162306a36Sopenharmony_ci * in the asm statement. 39262306a36Sopenharmony_ci */ 39362306a36Sopenharmony_ci switch (ric) { 39462306a36Sopenharmony_ci case RIC_FLUSH_TLB: 39562306a36Sopenharmony_ci __tlbie_lpid(lpid, RIC_FLUSH_TLB); 39662306a36Sopenharmony_ci fixup_tlbie_lpid(lpid); 39762306a36Sopenharmony_ci break; 39862306a36Sopenharmony_ci case RIC_FLUSH_PWC: 39962306a36Sopenharmony_ci __tlbie_lpid(lpid, RIC_FLUSH_PWC); 40062306a36Sopenharmony_ci break; 40162306a36Sopenharmony_ci case RIC_FLUSH_ALL: 40262306a36Sopenharmony_ci default: 40362306a36Sopenharmony_ci __tlbie_lpid(lpid, RIC_FLUSH_ALL); 40462306a36Sopenharmony_ci fixup_tlbie_lpid(lpid); 40562306a36Sopenharmony_ci } 40662306a36Sopenharmony_ci asm volatile("eieio; tlbsync; ptesync": : :"memory"); 40762306a36Sopenharmony_ci} 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_cistatic __always_inline void _tlbie_lpid_guest(unsigned long lpid, unsigned long ric) 41062306a36Sopenharmony_ci{ 41162306a36Sopenharmony_ci /* 41262306a36Sopenharmony_ci * Workaround the fact that the "ric" argument to __tlbie_pid 41362306a36Sopenharmony_ci * must be a compile-time contraint to match the "i" constraint 41462306a36Sopenharmony_ci * in the asm statement. 41562306a36Sopenharmony_ci */ 41662306a36Sopenharmony_ci switch (ric) { 41762306a36Sopenharmony_ci case RIC_FLUSH_TLB: 41862306a36Sopenharmony_ci __tlbie_lpid_guest(lpid, RIC_FLUSH_TLB); 41962306a36Sopenharmony_ci break; 42062306a36Sopenharmony_ci case RIC_FLUSH_PWC: 42162306a36Sopenharmony_ci __tlbie_lpid_guest(lpid, RIC_FLUSH_PWC); 42262306a36Sopenharmony_ci break; 42362306a36Sopenharmony_ci case RIC_FLUSH_ALL: 42462306a36Sopenharmony_ci default: 42562306a36Sopenharmony_ci __tlbie_lpid_guest(lpid, RIC_FLUSH_ALL); 42662306a36Sopenharmony_ci } 42762306a36Sopenharmony_ci fixup_tlbie_lpid(lpid); 42862306a36Sopenharmony_ci asm volatile("eieio; tlbsync; ptesync": : :"memory"); 42962306a36Sopenharmony_ci} 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_cistatic inline void __tlbiel_va_range(unsigned long start, unsigned long end, 43262306a36Sopenharmony_ci unsigned long pid, unsigned long page_size, 43362306a36Sopenharmony_ci unsigned long psize) 43462306a36Sopenharmony_ci{ 43562306a36Sopenharmony_ci unsigned long addr; 43662306a36Sopenharmony_ci unsigned long ap = mmu_get_ap(psize); 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci for (addr = start; addr < end; addr += page_size) 43962306a36Sopenharmony_ci __tlbiel_va(addr, pid, ap, RIC_FLUSH_TLB); 44062306a36Sopenharmony_ci} 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_cistatic __always_inline void _tlbiel_va(unsigned long va, unsigned long pid, 44362306a36Sopenharmony_ci unsigned long psize, unsigned long ric) 44462306a36Sopenharmony_ci{ 44562306a36Sopenharmony_ci unsigned long ap = mmu_get_ap(psize); 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci asm volatile("ptesync": : :"memory"); 44862306a36Sopenharmony_ci __tlbiel_va(va, pid, ap, ric); 44962306a36Sopenharmony_ci ppc_after_tlbiel_barrier(); 45062306a36Sopenharmony_ci} 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_cistatic inline void _tlbiel_va_range(unsigned long start, unsigned long end, 45362306a36Sopenharmony_ci unsigned long pid, unsigned long page_size, 45462306a36Sopenharmony_ci unsigned long psize, bool also_pwc) 45562306a36Sopenharmony_ci{ 45662306a36Sopenharmony_ci asm volatile("ptesync": : :"memory"); 45762306a36Sopenharmony_ci if (also_pwc) 45862306a36Sopenharmony_ci __tlbiel_pid(pid, 0, RIC_FLUSH_PWC); 45962306a36Sopenharmony_ci __tlbiel_va_range(start, end, pid, page_size, psize); 46062306a36Sopenharmony_ci ppc_after_tlbiel_barrier(); 46162306a36Sopenharmony_ci} 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_cistatic inline void __tlbie_va_range(unsigned long start, unsigned long end, 46462306a36Sopenharmony_ci unsigned long pid, unsigned long page_size, 46562306a36Sopenharmony_ci unsigned long psize) 46662306a36Sopenharmony_ci{ 46762306a36Sopenharmony_ci unsigned long addr; 46862306a36Sopenharmony_ci unsigned long ap = mmu_get_ap(psize); 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci for (addr = start; addr < end; addr += page_size) 47162306a36Sopenharmony_ci __tlbie_va(addr, pid, ap, RIC_FLUSH_TLB); 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci fixup_tlbie_va_range(addr - page_size, pid, ap); 47462306a36Sopenharmony_ci} 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_cistatic __always_inline void _tlbie_va(unsigned long va, unsigned long pid, 47762306a36Sopenharmony_ci unsigned long psize, unsigned long ric) 47862306a36Sopenharmony_ci{ 47962306a36Sopenharmony_ci unsigned long ap = mmu_get_ap(psize); 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci asm volatile("ptesync": : :"memory"); 48262306a36Sopenharmony_ci __tlbie_va(va, pid, ap, ric); 48362306a36Sopenharmony_ci fixup_tlbie_va(va, pid, ap); 48462306a36Sopenharmony_ci asm volatile("eieio; tlbsync; ptesync": : :"memory"); 48562306a36Sopenharmony_ci} 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_cistruct tlbiel_va { 48862306a36Sopenharmony_ci unsigned long pid; 48962306a36Sopenharmony_ci unsigned long va; 49062306a36Sopenharmony_ci unsigned long psize; 49162306a36Sopenharmony_ci unsigned long ric; 49262306a36Sopenharmony_ci}; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_cistatic void do_tlbiel_va(void *info) 49562306a36Sopenharmony_ci{ 49662306a36Sopenharmony_ci struct tlbiel_va *t = info; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci if (t->ric == RIC_FLUSH_TLB) 49962306a36Sopenharmony_ci _tlbiel_va(t->va, t->pid, t->psize, RIC_FLUSH_TLB); 50062306a36Sopenharmony_ci else if (t->ric == RIC_FLUSH_PWC) 50162306a36Sopenharmony_ci _tlbiel_va(t->va, t->pid, t->psize, RIC_FLUSH_PWC); 50262306a36Sopenharmony_ci else 50362306a36Sopenharmony_ci _tlbiel_va(t->va, t->pid, t->psize, RIC_FLUSH_ALL); 50462306a36Sopenharmony_ci} 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_cistatic inline void _tlbiel_va_multicast(struct mm_struct *mm, 50762306a36Sopenharmony_ci unsigned long va, unsigned long pid, 50862306a36Sopenharmony_ci unsigned long psize, unsigned long ric) 50962306a36Sopenharmony_ci{ 51062306a36Sopenharmony_ci struct cpumask *cpus = mm_cpumask(mm); 51162306a36Sopenharmony_ci struct tlbiel_va t = { .va = va, .pid = pid, .psize = psize, .ric = ric }; 51262306a36Sopenharmony_ci on_each_cpu_mask(cpus, do_tlbiel_va, &t, 1); 51362306a36Sopenharmony_ci if (atomic_read(&mm->context.copros) > 0) 51462306a36Sopenharmony_ci _tlbie_va(va, pid, psize, RIC_FLUSH_TLB); 51562306a36Sopenharmony_ci} 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_cistruct tlbiel_va_range { 51862306a36Sopenharmony_ci unsigned long pid; 51962306a36Sopenharmony_ci unsigned long start; 52062306a36Sopenharmony_ci unsigned long end; 52162306a36Sopenharmony_ci unsigned long page_size; 52262306a36Sopenharmony_ci unsigned long psize; 52362306a36Sopenharmony_ci bool also_pwc; 52462306a36Sopenharmony_ci}; 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_cistatic void do_tlbiel_va_range(void *info) 52762306a36Sopenharmony_ci{ 52862306a36Sopenharmony_ci struct tlbiel_va_range *t = info; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci _tlbiel_va_range(t->start, t->end, t->pid, t->page_size, 53162306a36Sopenharmony_ci t->psize, t->also_pwc); 53262306a36Sopenharmony_ci} 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_cistatic __always_inline void _tlbie_lpid_va(unsigned long va, unsigned long lpid, 53562306a36Sopenharmony_ci unsigned long psize, unsigned long ric) 53662306a36Sopenharmony_ci{ 53762306a36Sopenharmony_ci unsigned long ap = mmu_get_ap(psize); 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci asm volatile("ptesync": : :"memory"); 54062306a36Sopenharmony_ci __tlbie_lpid_va(va, lpid, ap, ric); 54162306a36Sopenharmony_ci fixup_tlbie_lpid_va(va, lpid, ap); 54262306a36Sopenharmony_ci asm volatile("eieio; tlbsync; ptesync": : :"memory"); 54362306a36Sopenharmony_ci} 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_cistatic inline void _tlbie_va_range(unsigned long start, unsigned long end, 54662306a36Sopenharmony_ci unsigned long pid, unsigned long page_size, 54762306a36Sopenharmony_ci unsigned long psize, bool also_pwc) 54862306a36Sopenharmony_ci{ 54962306a36Sopenharmony_ci asm volatile("ptesync": : :"memory"); 55062306a36Sopenharmony_ci if (also_pwc) 55162306a36Sopenharmony_ci __tlbie_pid(pid, RIC_FLUSH_PWC); 55262306a36Sopenharmony_ci __tlbie_va_range(start, end, pid, page_size, psize); 55362306a36Sopenharmony_ci asm volatile("eieio; tlbsync; ptesync": : :"memory"); 55462306a36Sopenharmony_ci} 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_cistatic inline void _tlbiel_va_range_multicast(struct mm_struct *mm, 55762306a36Sopenharmony_ci unsigned long start, unsigned long end, 55862306a36Sopenharmony_ci unsigned long pid, unsigned long page_size, 55962306a36Sopenharmony_ci unsigned long psize, bool also_pwc) 56062306a36Sopenharmony_ci{ 56162306a36Sopenharmony_ci struct cpumask *cpus = mm_cpumask(mm); 56262306a36Sopenharmony_ci struct tlbiel_va_range t = { .start = start, .end = end, 56362306a36Sopenharmony_ci .pid = pid, .page_size = page_size, 56462306a36Sopenharmony_ci .psize = psize, .also_pwc = also_pwc }; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci on_each_cpu_mask(cpus, do_tlbiel_va_range, &t, 1); 56762306a36Sopenharmony_ci if (atomic_read(&mm->context.copros) > 0) 56862306a36Sopenharmony_ci _tlbie_va_range(start, end, pid, page_size, psize, also_pwc); 56962306a36Sopenharmony_ci} 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci/* 57262306a36Sopenharmony_ci * Base TLB flushing operations: 57362306a36Sopenharmony_ci * 57462306a36Sopenharmony_ci * - flush_tlb_mm(mm) flushes the specified mm context TLB's 57562306a36Sopenharmony_ci * - flush_tlb_page(vma, vmaddr) flushes one page 57662306a36Sopenharmony_ci * - flush_tlb_range(vma, start, end) flushes a range of pages 57762306a36Sopenharmony_ci * - flush_tlb_kernel_range(start, end) flushes kernel pages 57862306a36Sopenharmony_ci * 57962306a36Sopenharmony_ci * - local_* variants of page and mm only apply to the current 58062306a36Sopenharmony_ci * processor 58162306a36Sopenharmony_ci */ 58262306a36Sopenharmony_civoid radix__local_flush_tlb_mm(struct mm_struct *mm) 58362306a36Sopenharmony_ci{ 58462306a36Sopenharmony_ci unsigned long pid = mm->context.id; 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci if (WARN_ON_ONCE(pid == MMU_NO_CONTEXT)) 58762306a36Sopenharmony_ci return; 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci preempt_disable(); 59062306a36Sopenharmony_ci _tlbiel_pid(pid, RIC_FLUSH_TLB); 59162306a36Sopenharmony_ci preempt_enable(); 59262306a36Sopenharmony_ci} 59362306a36Sopenharmony_ciEXPORT_SYMBOL(radix__local_flush_tlb_mm); 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci#ifndef CONFIG_SMP 59662306a36Sopenharmony_civoid radix__local_flush_all_mm(struct mm_struct *mm) 59762306a36Sopenharmony_ci{ 59862306a36Sopenharmony_ci unsigned long pid = mm->context.id; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci if (WARN_ON_ONCE(pid == MMU_NO_CONTEXT)) 60162306a36Sopenharmony_ci return; 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci preempt_disable(); 60462306a36Sopenharmony_ci _tlbiel_pid(pid, RIC_FLUSH_ALL); 60562306a36Sopenharmony_ci preempt_enable(); 60662306a36Sopenharmony_ci} 60762306a36Sopenharmony_ciEXPORT_SYMBOL(radix__local_flush_all_mm); 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_cistatic void __flush_all_mm(struct mm_struct *mm, bool fullmm) 61062306a36Sopenharmony_ci{ 61162306a36Sopenharmony_ci radix__local_flush_all_mm(mm); 61262306a36Sopenharmony_ci} 61362306a36Sopenharmony_ci#endif /* CONFIG_SMP */ 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_civoid radix__local_flush_tlb_page_psize(struct mm_struct *mm, unsigned long vmaddr, 61662306a36Sopenharmony_ci int psize) 61762306a36Sopenharmony_ci{ 61862306a36Sopenharmony_ci unsigned long pid = mm->context.id; 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci if (WARN_ON_ONCE(pid == MMU_NO_CONTEXT)) 62162306a36Sopenharmony_ci return; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci preempt_disable(); 62462306a36Sopenharmony_ci _tlbiel_va(vmaddr, pid, psize, RIC_FLUSH_TLB); 62562306a36Sopenharmony_ci preempt_enable(); 62662306a36Sopenharmony_ci} 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_civoid radix__local_flush_tlb_page(struct vm_area_struct *vma, unsigned long vmaddr) 62962306a36Sopenharmony_ci{ 63062306a36Sopenharmony_ci#ifdef CONFIG_HUGETLB_PAGE 63162306a36Sopenharmony_ci /* need the return fix for nohash.c */ 63262306a36Sopenharmony_ci if (is_vm_hugetlb_page(vma)) 63362306a36Sopenharmony_ci return radix__local_flush_hugetlb_page(vma, vmaddr); 63462306a36Sopenharmony_ci#endif 63562306a36Sopenharmony_ci radix__local_flush_tlb_page_psize(vma->vm_mm, vmaddr, mmu_virtual_psize); 63662306a36Sopenharmony_ci} 63762306a36Sopenharmony_ciEXPORT_SYMBOL(radix__local_flush_tlb_page); 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_cistatic bool mm_needs_flush_escalation(struct mm_struct *mm) 64062306a36Sopenharmony_ci{ 64162306a36Sopenharmony_ci /* 64262306a36Sopenharmony_ci * The P9 nest MMU has issues with the page walk cache caching PTEs 64362306a36Sopenharmony_ci * and not flushing them when RIC = 0 for a PID/LPID invalidate. 64462306a36Sopenharmony_ci * 64562306a36Sopenharmony_ci * This may have been fixed in shipping firmware (by disabling PWC 64662306a36Sopenharmony_ci * or preventing it from caching PTEs), but until that is confirmed, 64762306a36Sopenharmony_ci * this workaround is required - escalate all RIC=0 IS=1/2/3 flushes 64862306a36Sopenharmony_ci * to RIC=2. 64962306a36Sopenharmony_ci * 65062306a36Sopenharmony_ci * POWER10 (and P9P) does not have this problem. 65162306a36Sopenharmony_ci */ 65262306a36Sopenharmony_ci if (cpu_has_feature(CPU_FTR_ARCH_31)) 65362306a36Sopenharmony_ci return false; 65462306a36Sopenharmony_ci if (atomic_read(&mm->context.copros) > 0) 65562306a36Sopenharmony_ci return true; 65662306a36Sopenharmony_ci return false; 65762306a36Sopenharmony_ci} 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci/* 66062306a36Sopenharmony_ci * If always_flush is true, then flush even if this CPU can't be removed 66162306a36Sopenharmony_ci * from mm_cpumask. 66262306a36Sopenharmony_ci */ 66362306a36Sopenharmony_civoid exit_lazy_flush_tlb(struct mm_struct *mm, bool always_flush) 66462306a36Sopenharmony_ci{ 66562306a36Sopenharmony_ci unsigned long pid = mm->context.id; 66662306a36Sopenharmony_ci int cpu = smp_processor_id(); 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci /* 66962306a36Sopenharmony_ci * A kthread could have done a mmget_not_zero() after the flushing CPU 67062306a36Sopenharmony_ci * checked mm_cpumask, and be in the process of kthread_use_mm when 67162306a36Sopenharmony_ci * interrupted here. In that case, current->mm will be set to mm, 67262306a36Sopenharmony_ci * because kthread_use_mm() setting ->mm and switching to the mm is 67362306a36Sopenharmony_ci * done with interrupts off. 67462306a36Sopenharmony_ci */ 67562306a36Sopenharmony_ci if (current->mm == mm) 67662306a36Sopenharmony_ci goto out; 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci if (current->active_mm == mm) { 67962306a36Sopenharmony_ci unsigned long flags; 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci WARN_ON_ONCE(current->mm != NULL); 68262306a36Sopenharmony_ci /* 68362306a36Sopenharmony_ci * It is a kernel thread and is using mm as the lazy tlb, so 68462306a36Sopenharmony_ci * switch it to init_mm. This is not always called from IPI 68562306a36Sopenharmony_ci * (e.g., flush_type_needed), so must disable irqs. 68662306a36Sopenharmony_ci */ 68762306a36Sopenharmony_ci local_irq_save(flags); 68862306a36Sopenharmony_ci mmgrab_lazy_tlb(&init_mm); 68962306a36Sopenharmony_ci current->active_mm = &init_mm; 69062306a36Sopenharmony_ci switch_mm_irqs_off(mm, &init_mm, current); 69162306a36Sopenharmony_ci mmdrop_lazy_tlb(mm); 69262306a36Sopenharmony_ci local_irq_restore(flags); 69362306a36Sopenharmony_ci } 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci /* 69662306a36Sopenharmony_ci * This IPI may be initiated from any source including those not 69762306a36Sopenharmony_ci * running the mm, so there may be a racing IPI that comes after 69862306a36Sopenharmony_ci * this one which finds the cpumask already clear. Check and avoid 69962306a36Sopenharmony_ci * underflowing the active_cpus count in that case. The race should 70062306a36Sopenharmony_ci * not otherwise be a problem, but the TLB must be flushed because 70162306a36Sopenharmony_ci * that's what the caller expects. 70262306a36Sopenharmony_ci */ 70362306a36Sopenharmony_ci if (cpumask_test_cpu(cpu, mm_cpumask(mm))) { 70462306a36Sopenharmony_ci dec_mm_active_cpus(mm); 70562306a36Sopenharmony_ci cpumask_clear_cpu(cpu, mm_cpumask(mm)); 70662306a36Sopenharmony_ci always_flush = true; 70762306a36Sopenharmony_ci } 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ciout: 71062306a36Sopenharmony_ci if (always_flush) 71162306a36Sopenharmony_ci _tlbiel_pid(pid, RIC_FLUSH_ALL); 71262306a36Sopenharmony_ci} 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci#ifdef CONFIG_SMP 71562306a36Sopenharmony_cistatic void do_exit_flush_lazy_tlb(void *arg) 71662306a36Sopenharmony_ci{ 71762306a36Sopenharmony_ci struct mm_struct *mm = arg; 71862306a36Sopenharmony_ci exit_lazy_flush_tlb(mm, true); 71962306a36Sopenharmony_ci} 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_cistatic void exit_flush_lazy_tlbs(struct mm_struct *mm) 72262306a36Sopenharmony_ci{ 72362306a36Sopenharmony_ci /* 72462306a36Sopenharmony_ci * Would be nice if this was async so it could be run in 72562306a36Sopenharmony_ci * parallel with our local flush, but generic code does not 72662306a36Sopenharmony_ci * give a good API for it. Could extend the generic code or 72762306a36Sopenharmony_ci * make a special powerpc IPI for flushing TLBs. 72862306a36Sopenharmony_ci * For now it's not too performance critical. 72962306a36Sopenharmony_ci */ 73062306a36Sopenharmony_ci smp_call_function_many(mm_cpumask(mm), do_exit_flush_lazy_tlb, 73162306a36Sopenharmony_ci (void *)mm, 1); 73262306a36Sopenharmony_ci} 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci#else /* CONFIG_SMP */ 73562306a36Sopenharmony_cistatic inline void exit_flush_lazy_tlbs(struct mm_struct *mm) { } 73662306a36Sopenharmony_ci#endif /* CONFIG_SMP */ 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_cistatic DEFINE_PER_CPU(unsigned int, mm_cpumask_trim_clock); 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci/* 74162306a36Sopenharmony_ci * Interval between flushes at which we send out IPIs to check whether the 74262306a36Sopenharmony_ci * mm_cpumask can be trimmed for the case where it's not a single-threaded 74362306a36Sopenharmony_ci * process flushing its own mm. The intent is to reduce the cost of later 74462306a36Sopenharmony_ci * flushes. Don't want this to be so low that it adds noticable cost to TLB 74562306a36Sopenharmony_ci * flushing, or so high that it doesn't help reduce global TLBIEs. 74662306a36Sopenharmony_ci */ 74762306a36Sopenharmony_cistatic unsigned long tlb_mm_cpumask_trim_timer = 1073; 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_cistatic bool tick_and_test_trim_clock(void) 75062306a36Sopenharmony_ci{ 75162306a36Sopenharmony_ci if (__this_cpu_inc_return(mm_cpumask_trim_clock) == 75262306a36Sopenharmony_ci tlb_mm_cpumask_trim_timer) { 75362306a36Sopenharmony_ci __this_cpu_write(mm_cpumask_trim_clock, 0); 75462306a36Sopenharmony_ci return true; 75562306a36Sopenharmony_ci } 75662306a36Sopenharmony_ci return false; 75762306a36Sopenharmony_ci} 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_cienum tlb_flush_type { 76062306a36Sopenharmony_ci FLUSH_TYPE_NONE, 76162306a36Sopenharmony_ci FLUSH_TYPE_LOCAL, 76262306a36Sopenharmony_ci FLUSH_TYPE_GLOBAL, 76362306a36Sopenharmony_ci}; 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_cistatic enum tlb_flush_type flush_type_needed(struct mm_struct *mm, bool fullmm) 76662306a36Sopenharmony_ci{ 76762306a36Sopenharmony_ci int active_cpus = atomic_read(&mm->context.active_cpus); 76862306a36Sopenharmony_ci int cpu = smp_processor_id(); 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci if (active_cpus == 0) 77162306a36Sopenharmony_ci return FLUSH_TYPE_NONE; 77262306a36Sopenharmony_ci if (active_cpus == 1 && cpumask_test_cpu(cpu, mm_cpumask(mm))) { 77362306a36Sopenharmony_ci if (current->mm != mm) { 77462306a36Sopenharmony_ci /* 77562306a36Sopenharmony_ci * Asynchronous flush sources may trim down to nothing 77662306a36Sopenharmony_ci * if the process is not running, so occasionally try 77762306a36Sopenharmony_ci * to trim. 77862306a36Sopenharmony_ci */ 77962306a36Sopenharmony_ci if (tick_and_test_trim_clock()) { 78062306a36Sopenharmony_ci exit_lazy_flush_tlb(mm, true); 78162306a36Sopenharmony_ci return FLUSH_TYPE_NONE; 78262306a36Sopenharmony_ci } 78362306a36Sopenharmony_ci } 78462306a36Sopenharmony_ci return FLUSH_TYPE_LOCAL; 78562306a36Sopenharmony_ci } 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci /* Coprocessors require TLBIE to invalidate nMMU. */ 78862306a36Sopenharmony_ci if (atomic_read(&mm->context.copros) > 0) 78962306a36Sopenharmony_ci return FLUSH_TYPE_GLOBAL; 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci /* 79262306a36Sopenharmony_ci * In the fullmm case there's no point doing the exit_flush_lazy_tlbs 79362306a36Sopenharmony_ci * because the mm is being taken down anyway, and a TLBIE tends to 79462306a36Sopenharmony_ci * be faster than an IPI+TLBIEL. 79562306a36Sopenharmony_ci */ 79662306a36Sopenharmony_ci if (fullmm) 79762306a36Sopenharmony_ci return FLUSH_TYPE_GLOBAL; 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci /* 80062306a36Sopenharmony_ci * If we are running the only thread of a single-threaded process, 80162306a36Sopenharmony_ci * then we should almost always be able to trim off the rest of the 80262306a36Sopenharmony_ci * CPU mask (except in the case of use_mm() races), so always try 80362306a36Sopenharmony_ci * trimming the mask. 80462306a36Sopenharmony_ci */ 80562306a36Sopenharmony_ci if (atomic_read(&mm->mm_users) <= 1 && current->mm == mm) { 80662306a36Sopenharmony_ci exit_flush_lazy_tlbs(mm); 80762306a36Sopenharmony_ci /* 80862306a36Sopenharmony_ci * use_mm() race could prevent IPIs from being able to clear 80962306a36Sopenharmony_ci * the cpumask here, however those users are established 81062306a36Sopenharmony_ci * after our first check (and so after the PTEs are removed), 81162306a36Sopenharmony_ci * and the TLB still gets flushed by the IPI, so this CPU 81262306a36Sopenharmony_ci * will only require a local flush. 81362306a36Sopenharmony_ci */ 81462306a36Sopenharmony_ci return FLUSH_TYPE_LOCAL; 81562306a36Sopenharmony_ci } 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci /* 81862306a36Sopenharmony_ci * Occasionally try to trim down the cpumask. It's possible this can 81962306a36Sopenharmony_ci * bring the mask to zero, which results in no flush. 82062306a36Sopenharmony_ci */ 82162306a36Sopenharmony_ci if (tick_and_test_trim_clock()) { 82262306a36Sopenharmony_ci exit_flush_lazy_tlbs(mm); 82362306a36Sopenharmony_ci if (current->mm == mm) 82462306a36Sopenharmony_ci return FLUSH_TYPE_LOCAL; 82562306a36Sopenharmony_ci if (cpumask_test_cpu(cpu, mm_cpumask(mm))) 82662306a36Sopenharmony_ci exit_lazy_flush_tlb(mm, true); 82762306a36Sopenharmony_ci return FLUSH_TYPE_NONE; 82862306a36Sopenharmony_ci } 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci return FLUSH_TYPE_GLOBAL; 83162306a36Sopenharmony_ci} 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci#ifdef CONFIG_SMP 83462306a36Sopenharmony_civoid radix__flush_tlb_mm(struct mm_struct *mm) 83562306a36Sopenharmony_ci{ 83662306a36Sopenharmony_ci unsigned long pid; 83762306a36Sopenharmony_ci enum tlb_flush_type type; 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci pid = mm->context.id; 84062306a36Sopenharmony_ci if (WARN_ON_ONCE(pid == MMU_NO_CONTEXT)) 84162306a36Sopenharmony_ci return; 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci preempt_disable(); 84462306a36Sopenharmony_ci /* 84562306a36Sopenharmony_ci * Order loads of mm_cpumask (in flush_type_needed) vs previous 84662306a36Sopenharmony_ci * stores to clear ptes before the invalidate. See barrier in 84762306a36Sopenharmony_ci * switch_mm_irqs_off 84862306a36Sopenharmony_ci */ 84962306a36Sopenharmony_ci smp_mb(); 85062306a36Sopenharmony_ci type = flush_type_needed(mm, false); 85162306a36Sopenharmony_ci if (type == FLUSH_TYPE_LOCAL) { 85262306a36Sopenharmony_ci _tlbiel_pid(pid, RIC_FLUSH_TLB); 85362306a36Sopenharmony_ci } else if (type == FLUSH_TYPE_GLOBAL) { 85462306a36Sopenharmony_ci if (!mmu_has_feature(MMU_FTR_GTSE)) { 85562306a36Sopenharmony_ci unsigned long tgt = H_RPTI_TARGET_CMMU; 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci if (atomic_read(&mm->context.copros) > 0) 85862306a36Sopenharmony_ci tgt |= H_RPTI_TARGET_NMMU; 85962306a36Sopenharmony_ci pseries_rpt_invalidate(pid, tgt, H_RPTI_TYPE_TLB, 86062306a36Sopenharmony_ci H_RPTI_PAGE_ALL, 0, -1UL); 86162306a36Sopenharmony_ci } else if (cputlb_use_tlbie()) { 86262306a36Sopenharmony_ci if (mm_needs_flush_escalation(mm)) 86362306a36Sopenharmony_ci _tlbie_pid(pid, RIC_FLUSH_ALL); 86462306a36Sopenharmony_ci else 86562306a36Sopenharmony_ci _tlbie_pid(pid, RIC_FLUSH_TLB); 86662306a36Sopenharmony_ci } else { 86762306a36Sopenharmony_ci _tlbiel_pid_multicast(mm, pid, RIC_FLUSH_TLB); 86862306a36Sopenharmony_ci } 86962306a36Sopenharmony_ci } 87062306a36Sopenharmony_ci preempt_enable(); 87162306a36Sopenharmony_ci mmu_notifier_arch_invalidate_secondary_tlbs(mm, 0, -1UL); 87262306a36Sopenharmony_ci} 87362306a36Sopenharmony_ciEXPORT_SYMBOL(radix__flush_tlb_mm); 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_cistatic void __flush_all_mm(struct mm_struct *mm, bool fullmm) 87662306a36Sopenharmony_ci{ 87762306a36Sopenharmony_ci unsigned long pid; 87862306a36Sopenharmony_ci enum tlb_flush_type type; 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci pid = mm->context.id; 88162306a36Sopenharmony_ci if (WARN_ON_ONCE(pid == MMU_NO_CONTEXT)) 88262306a36Sopenharmony_ci return; 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci preempt_disable(); 88562306a36Sopenharmony_ci smp_mb(); /* see radix__flush_tlb_mm */ 88662306a36Sopenharmony_ci type = flush_type_needed(mm, fullmm); 88762306a36Sopenharmony_ci if (type == FLUSH_TYPE_LOCAL) { 88862306a36Sopenharmony_ci _tlbiel_pid(pid, RIC_FLUSH_ALL); 88962306a36Sopenharmony_ci } else if (type == FLUSH_TYPE_GLOBAL) { 89062306a36Sopenharmony_ci if (!mmu_has_feature(MMU_FTR_GTSE)) { 89162306a36Sopenharmony_ci unsigned long tgt = H_RPTI_TARGET_CMMU; 89262306a36Sopenharmony_ci unsigned long type = H_RPTI_TYPE_TLB | H_RPTI_TYPE_PWC | 89362306a36Sopenharmony_ci H_RPTI_TYPE_PRT; 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci if (atomic_read(&mm->context.copros) > 0) 89662306a36Sopenharmony_ci tgt |= H_RPTI_TARGET_NMMU; 89762306a36Sopenharmony_ci pseries_rpt_invalidate(pid, tgt, type, 89862306a36Sopenharmony_ci H_RPTI_PAGE_ALL, 0, -1UL); 89962306a36Sopenharmony_ci } else if (cputlb_use_tlbie()) 90062306a36Sopenharmony_ci _tlbie_pid(pid, RIC_FLUSH_ALL); 90162306a36Sopenharmony_ci else 90262306a36Sopenharmony_ci _tlbiel_pid_multicast(mm, pid, RIC_FLUSH_ALL); 90362306a36Sopenharmony_ci } 90462306a36Sopenharmony_ci preempt_enable(); 90562306a36Sopenharmony_ci mmu_notifier_arch_invalidate_secondary_tlbs(mm, 0, -1UL); 90662306a36Sopenharmony_ci} 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_civoid radix__flush_all_mm(struct mm_struct *mm) 90962306a36Sopenharmony_ci{ 91062306a36Sopenharmony_ci __flush_all_mm(mm, false); 91162306a36Sopenharmony_ci} 91262306a36Sopenharmony_ciEXPORT_SYMBOL(radix__flush_all_mm); 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_civoid radix__flush_tlb_page_psize(struct mm_struct *mm, unsigned long vmaddr, 91562306a36Sopenharmony_ci int psize) 91662306a36Sopenharmony_ci{ 91762306a36Sopenharmony_ci unsigned long pid; 91862306a36Sopenharmony_ci enum tlb_flush_type type; 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci pid = mm->context.id; 92162306a36Sopenharmony_ci if (WARN_ON_ONCE(pid == MMU_NO_CONTEXT)) 92262306a36Sopenharmony_ci return; 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci preempt_disable(); 92562306a36Sopenharmony_ci smp_mb(); /* see radix__flush_tlb_mm */ 92662306a36Sopenharmony_ci type = flush_type_needed(mm, false); 92762306a36Sopenharmony_ci if (type == FLUSH_TYPE_LOCAL) { 92862306a36Sopenharmony_ci _tlbiel_va(vmaddr, pid, psize, RIC_FLUSH_TLB); 92962306a36Sopenharmony_ci } else if (type == FLUSH_TYPE_GLOBAL) { 93062306a36Sopenharmony_ci if (!mmu_has_feature(MMU_FTR_GTSE)) { 93162306a36Sopenharmony_ci unsigned long tgt, pg_sizes, size; 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ci tgt = H_RPTI_TARGET_CMMU; 93462306a36Sopenharmony_ci pg_sizes = psize_to_rpti_pgsize(psize); 93562306a36Sopenharmony_ci size = 1UL << mmu_psize_to_shift(psize); 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci if (atomic_read(&mm->context.copros) > 0) 93862306a36Sopenharmony_ci tgt |= H_RPTI_TARGET_NMMU; 93962306a36Sopenharmony_ci pseries_rpt_invalidate(pid, tgt, H_RPTI_TYPE_TLB, 94062306a36Sopenharmony_ci pg_sizes, vmaddr, 94162306a36Sopenharmony_ci vmaddr + size); 94262306a36Sopenharmony_ci } else if (cputlb_use_tlbie()) 94362306a36Sopenharmony_ci _tlbie_va(vmaddr, pid, psize, RIC_FLUSH_TLB); 94462306a36Sopenharmony_ci else 94562306a36Sopenharmony_ci _tlbiel_va_multicast(mm, vmaddr, pid, psize, RIC_FLUSH_TLB); 94662306a36Sopenharmony_ci } 94762306a36Sopenharmony_ci preempt_enable(); 94862306a36Sopenharmony_ci} 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_civoid radix__flush_tlb_page(struct vm_area_struct *vma, unsigned long vmaddr) 95162306a36Sopenharmony_ci{ 95262306a36Sopenharmony_ci#ifdef CONFIG_HUGETLB_PAGE 95362306a36Sopenharmony_ci if (is_vm_hugetlb_page(vma)) 95462306a36Sopenharmony_ci return radix__flush_hugetlb_page(vma, vmaddr); 95562306a36Sopenharmony_ci#endif 95662306a36Sopenharmony_ci radix__flush_tlb_page_psize(vma->vm_mm, vmaddr, mmu_virtual_psize); 95762306a36Sopenharmony_ci} 95862306a36Sopenharmony_ciEXPORT_SYMBOL(radix__flush_tlb_page); 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ci#endif /* CONFIG_SMP */ 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_cistatic void do_tlbiel_kernel(void *info) 96362306a36Sopenharmony_ci{ 96462306a36Sopenharmony_ci _tlbiel_pid(0, RIC_FLUSH_ALL); 96562306a36Sopenharmony_ci} 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_cistatic inline void _tlbiel_kernel_broadcast(void) 96862306a36Sopenharmony_ci{ 96962306a36Sopenharmony_ci on_each_cpu(do_tlbiel_kernel, NULL, 1); 97062306a36Sopenharmony_ci if (tlbie_capable) { 97162306a36Sopenharmony_ci /* 97262306a36Sopenharmony_ci * Coherent accelerators don't refcount kernel memory mappings, 97362306a36Sopenharmony_ci * so have to always issue a tlbie for them. This is quite a 97462306a36Sopenharmony_ci * slow path anyway. 97562306a36Sopenharmony_ci */ 97662306a36Sopenharmony_ci _tlbie_pid(0, RIC_FLUSH_ALL); 97762306a36Sopenharmony_ci } 97862306a36Sopenharmony_ci} 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci/* 98162306a36Sopenharmony_ci * If kernel TLBIs ever become local rather than global, then 98262306a36Sopenharmony_ci * drivers/misc/ocxl/link.c:ocxl_link_add_pe will need some work, as it 98362306a36Sopenharmony_ci * assumes kernel TLBIs are global. 98462306a36Sopenharmony_ci */ 98562306a36Sopenharmony_civoid radix__flush_tlb_kernel_range(unsigned long start, unsigned long end) 98662306a36Sopenharmony_ci{ 98762306a36Sopenharmony_ci if (!mmu_has_feature(MMU_FTR_GTSE)) { 98862306a36Sopenharmony_ci unsigned long tgt = H_RPTI_TARGET_CMMU | H_RPTI_TARGET_NMMU; 98962306a36Sopenharmony_ci unsigned long type = H_RPTI_TYPE_TLB | H_RPTI_TYPE_PWC | 99062306a36Sopenharmony_ci H_RPTI_TYPE_PRT; 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_ci pseries_rpt_invalidate(0, tgt, type, H_RPTI_PAGE_ALL, 99362306a36Sopenharmony_ci start, end); 99462306a36Sopenharmony_ci } else if (cputlb_use_tlbie()) 99562306a36Sopenharmony_ci _tlbie_pid(0, RIC_FLUSH_ALL); 99662306a36Sopenharmony_ci else 99762306a36Sopenharmony_ci _tlbiel_kernel_broadcast(); 99862306a36Sopenharmony_ci} 99962306a36Sopenharmony_ciEXPORT_SYMBOL(radix__flush_tlb_kernel_range); 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci/* 100262306a36Sopenharmony_ci * Doesn't appear to be used anywhere. Remove. 100362306a36Sopenharmony_ci */ 100462306a36Sopenharmony_ci#define TLB_FLUSH_ALL -1UL 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_ci/* 100762306a36Sopenharmony_ci * Number of pages above which we invalidate the entire PID rather than 100862306a36Sopenharmony_ci * flush individual pages, for local and global flushes respectively. 100962306a36Sopenharmony_ci * 101062306a36Sopenharmony_ci * tlbie goes out to the interconnect and individual ops are more costly. 101162306a36Sopenharmony_ci * It also does not iterate over sets like the local tlbiel variant when 101262306a36Sopenharmony_ci * invalidating a full PID, so it has a far lower threshold to change from 101362306a36Sopenharmony_ci * individual page flushes to full-pid flushes. 101462306a36Sopenharmony_ci */ 101562306a36Sopenharmony_cistatic u32 tlb_single_page_flush_ceiling __read_mostly = 33; 101662306a36Sopenharmony_cistatic u32 tlb_local_single_page_flush_ceiling __read_mostly = POWER9_TLB_SETS_RADIX * 2; 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_cistatic inline void __radix__flush_tlb_range(struct mm_struct *mm, 101962306a36Sopenharmony_ci unsigned long start, unsigned long end) 102062306a36Sopenharmony_ci{ 102162306a36Sopenharmony_ci unsigned long pid; 102262306a36Sopenharmony_ci unsigned int page_shift = mmu_psize_defs[mmu_virtual_psize].shift; 102362306a36Sopenharmony_ci unsigned long page_size = 1UL << page_shift; 102462306a36Sopenharmony_ci unsigned long nr_pages = (end - start) >> page_shift; 102562306a36Sopenharmony_ci bool flush_pid, flush_pwc = false; 102662306a36Sopenharmony_ci enum tlb_flush_type type; 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ci pid = mm->context.id; 102962306a36Sopenharmony_ci if (WARN_ON_ONCE(pid == MMU_NO_CONTEXT)) 103062306a36Sopenharmony_ci return; 103162306a36Sopenharmony_ci 103262306a36Sopenharmony_ci WARN_ON_ONCE(end == TLB_FLUSH_ALL); 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci preempt_disable(); 103562306a36Sopenharmony_ci smp_mb(); /* see radix__flush_tlb_mm */ 103662306a36Sopenharmony_ci type = flush_type_needed(mm, false); 103762306a36Sopenharmony_ci if (type == FLUSH_TYPE_NONE) 103862306a36Sopenharmony_ci goto out; 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci if (type == FLUSH_TYPE_GLOBAL) 104162306a36Sopenharmony_ci flush_pid = nr_pages > tlb_single_page_flush_ceiling; 104262306a36Sopenharmony_ci else 104362306a36Sopenharmony_ci flush_pid = nr_pages > tlb_local_single_page_flush_ceiling; 104462306a36Sopenharmony_ci /* 104562306a36Sopenharmony_ci * full pid flush already does the PWC flush. if it is not full pid 104662306a36Sopenharmony_ci * flush check the range is more than PMD and force a pwc flush 104762306a36Sopenharmony_ci * mremap() depends on this behaviour. 104862306a36Sopenharmony_ci */ 104962306a36Sopenharmony_ci if (!flush_pid && (end - start) >= PMD_SIZE) 105062306a36Sopenharmony_ci flush_pwc = true; 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ci if (!mmu_has_feature(MMU_FTR_GTSE) && type == FLUSH_TYPE_GLOBAL) { 105362306a36Sopenharmony_ci unsigned long type = H_RPTI_TYPE_TLB; 105462306a36Sopenharmony_ci unsigned long tgt = H_RPTI_TARGET_CMMU; 105562306a36Sopenharmony_ci unsigned long pg_sizes = psize_to_rpti_pgsize(mmu_virtual_psize); 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE)) 105862306a36Sopenharmony_ci pg_sizes |= psize_to_rpti_pgsize(MMU_PAGE_2M); 105962306a36Sopenharmony_ci if (atomic_read(&mm->context.copros) > 0) 106062306a36Sopenharmony_ci tgt |= H_RPTI_TARGET_NMMU; 106162306a36Sopenharmony_ci if (flush_pwc) 106262306a36Sopenharmony_ci type |= H_RPTI_TYPE_PWC; 106362306a36Sopenharmony_ci pseries_rpt_invalidate(pid, tgt, type, pg_sizes, start, end); 106462306a36Sopenharmony_ci } else if (flush_pid) { 106562306a36Sopenharmony_ci /* 106662306a36Sopenharmony_ci * We are now flushing a range larger than PMD size force a RIC_FLUSH_ALL 106762306a36Sopenharmony_ci */ 106862306a36Sopenharmony_ci if (type == FLUSH_TYPE_LOCAL) { 106962306a36Sopenharmony_ci _tlbiel_pid(pid, RIC_FLUSH_ALL); 107062306a36Sopenharmony_ci } else { 107162306a36Sopenharmony_ci if (cputlb_use_tlbie()) { 107262306a36Sopenharmony_ci _tlbie_pid(pid, RIC_FLUSH_ALL); 107362306a36Sopenharmony_ci } else { 107462306a36Sopenharmony_ci _tlbiel_pid_multicast(mm, pid, RIC_FLUSH_ALL); 107562306a36Sopenharmony_ci } 107662306a36Sopenharmony_ci } 107762306a36Sopenharmony_ci } else { 107862306a36Sopenharmony_ci bool hflush; 107962306a36Sopenharmony_ci unsigned long hstart, hend; 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_ci hstart = (start + PMD_SIZE - 1) & PMD_MASK; 108262306a36Sopenharmony_ci hend = end & PMD_MASK; 108362306a36Sopenharmony_ci hflush = IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) && hstart < hend; 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci if (type == FLUSH_TYPE_LOCAL) { 108662306a36Sopenharmony_ci asm volatile("ptesync": : :"memory"); 108762306a36Sopenharmony_ci if (flush_pwc) 108862306a36Sopenharmony_ci /* For PWC, only one flush is needed */ 108962306a36Sopenharmony_ci __tlbiel_pid(pid, 0, RIC_FLUSH_PWC); 109062306a36Sopenharmony_ci __tlbiel_va_range(start, end, pid, page_size, mmu_virtual_psize); 109162306a36Sopenharmony_ci if (hflush) 109262306a36Sopenharmony_ci __tlbiel_va_range(hstart, hend, pid, 109362306a36Sopenharmony_ci PMD_SIZE, MMU_PAGE_2M); 109462306a36Sopenharmony_ci ppc_after_tlbiel_barrier(); 109562306a36Sopenharmony_ci } else if (cputlb_use_tlbie()) { 109662306a36Sopenharmony_ci asm volatile("ptesync": : :"memory"); 109762306a36Sopenharmony_ci if (flush_pwc) 109862306a36Sopenharmony_ci __tlbie_pid(pid, RIC_FLUSH_PWC); 109962306a36Sopenharmony_ci __tlbie_va_range(start, end, pid, page_size, mmu_virtual_psize); 110062306a36Sopenharmony_ci if (hflush) 110162306a36Sopenharmony_ci __tlbie_va_range(hstart, hend, pid, 110262306a36Sopenharmony_ci PMD_SIZE, MMU_PAGE_2M); 110362306a36Sopenharmony_ci asm volatile("eieio; tlbsync; ptesync": : :"memory"); 110462306a36Sopenharmony_ci } else { 110562306a36Sopenharmony_ci _tlbiel_va_range_multicast(mm, 110662306a36Sopenharmony_ci start, end, pid, page_size, mmu_virtual_psize, flush_pwc); 110762306a36Sopenharmony_ci if (hflush) 110862306a36Sopenharmony_ci _tlbiel_va_range_multicast(mm, 110962306a36Sopenharmony_ci hstart, hend, pid, PMD_SIZE, MMU_PAGE_2M, flush_pwc); 111062306a36Sopenharmony_ci } 111162306a36Sopenharmony_ci } 111262306a36Sopenharmony_ciout: 111362306a36Sopenharmony_ci preempt_enable(); 111462306a36Sopenharmony_ci mmu_notifier_arch_invalidate_secondary_tlbs(mm, start, end); 111562306a36Sopenharmony_ci} 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_civoid radix__flush_tlb_range(struct vm_area_struct *vma, unsigned long start, 111862306a36Sopenharmony_ci unsigned long end) 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_ci{ 112162306a36Sopenharmony_ci#ifdef CONFIG_HUGETLB_PAGE 112262306a36Sopenharmony_ci if (is_vm_hugetlb_page(vma)) 112362306a36Sopenharmony_ci return radix__flush_hugetlb_tlb_range(vma, start, end); 112462306a36Sopenharmony_ci#endif 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ci __radix__flush_tlb_range(vma->vm_mm, start, end); 112762306a36Sopenharmony_ci} 112862306a36Sopenharmony_ciEXPORT_SYMBOL(radix__flush_tlb_range); 112962306a36Sopenharmony_ci 113062306a36Sopenharmony_cistatic int radix_get_mmu_psize(int page_size) 113162306a36Sopenharmony_ci{ 113262306a36Sopenharmony_ci int psize; 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_ci if (page_size == (1UL << mmu_psize_defs[mmu_virtual_psize].shift)) 113562306a36Sopenharmony_ci psize = mmu_virtual_psize; 113662306a36Sopenharmony_ci else if (page_size == (1UL << mmu_psize_defs[MMU_PAGE_2M].shift)) 113762306a36Sopenharmony_ci psize = MMU_PAGE_2M; 113862306a36Sopenharmony_ci else if (page_size == (1UL << mmu_psize_defs[MMU_PAGE_1G].shift)) 113962306a36Sopenharmony_ci psize = MMU_PAGE_1G; 114062306a36Sopenharmony_ci else 114162306a36Sopenharmony_ci return -1; 114262306a36Sopenharmony_ci return psize; 114362306a36Sopenharmony_ci} 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_ci/* 114662306a36Sopenharmony_ci * Flush partition scoped LPID address translation for all CPUs. 114762306a36Sopenharmony_ci */ 114862306a36Sopenharmony_civoid radix__flush_tlb_lpid_page(unsigned int lpid, 114962306a36Sopenharmony_ci unsigned long addr, 115062306a36Sopenharmony_ci unsigned long page_size) 115162306a36Sopenharmony_ci{ 115262306a36Sopenharmony_ci int psize = radix_get_mmu_psize(page_size); 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_ci _tlbie_lpid_va(addr, lpid, psize, RIC_FLUSH_TLB); 115562306a36Sopenharmony_ci} 115662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(radix__flush_tlb_lpid_page); 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_ci/* 115962306a36Sopenharmony_ci * Flush partition scoped PWC from LPID for all CPUs. 116062306a36Sopenharmony_ci */ 116162306a36Sopenharmony_civoid radix__flush_pwc_lpid(unsigned int lpid) 116262306a36Sopenharmony_ci{ 116362306a36Sopenharmony_ci _tlbie_lpid(lpid, RIC_FLUSH_PWC); 116462306a36Sopenharmony_ci} 116562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(radix__flush_pwc_lpid); 116662306a36Sopenharmony_ci 116762306a36Sopenharmony_ci/* 116862306a36Sopenharmony_ci * Flush partition scoped translations from LPID (=LPIDR) 116962306a36Sopenharmony_ci */ 117062306a36Sopenharmony_civoid radix__flush_all_lpid(unsigned int lpid) 117162306a36Sopenharmony_ci{ 117262306a36Sopenharmony_ci _tlbie_lpid(lpid, RIC_FLUSH_ALL); 117362306a36Sopenharmony_ci} 117462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(radix__flush_all_lpid); 117562306a36Sopenharmony_ci 117662306a36Sopenharmony_ci/* 117762306a36Sopenharmony_ci * Flush process scoped translations from LPID (=LPIDR) 117862306a36Sopenharmony_ci */ 117962306a36Sopenharmony_civoid radix__flush_all_lpid_guest(unsigned int lpid) 118062306a36Sopenharmony_ci{ 118162306a36Sopenharmony_ci _tlbie_lpid_guest(lpid, RIC_FLUSH_ALL); 118262306a36Sopenharmony_ci} 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_civoid radix__tlb_flush(struct mmu_gather *tlb) 118562306a36Sopenharmony_ci{ 118662306a36Sopenharmony_ci int psize = 0; 118762306a36Sopenharmony_ci struct mm_struct *mm = tlb->mm; 118862306a36Sopenharmony_ci int page_size = tlb->page_size; 118962306a36Sopenharmony_ci unsigned long start = tlb->start; 119062306a36Sopenharmony_ci unsigned long end = tlb->end; 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_ci /* 119362306a36Sopenharmony_ci * if page size is not something we understand, do a full mm flush 119462306a36Sopenharmony_ci * 119562306a36Sopenharmony_ci * A "fullmm" flush must always do a flush_all_mm (RIC=2) flush 119662306a36Sopenharmony_ci * that flushes the process table entry cache upon process teardown. 119762306a36Sopenharmony_ci * See the comment for radix in arch_exit_mmap(). 119862306a36Sopenharmony_ci */ 119962306a36Sopenharmony_ci if (tlb->fullmm) { 120062306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_MMU_LAZY_TLB_SHOOTDOWN)) { 120162306a36Sopenharmony_ci /* 120262306a36Sopenharmony_ci * Shootdown based lazy tlb mm refcounting means we 120362306a36Sopenharmony_ci * have to IPI everyone in the mm_cpumask anyway soon 120462306a36Sopenharmony_ci * when the mm goes away, so might as well do it as 120562306a36Sopenharmony_ci * part of the final flush now. 120662306a36Sopenharmony_ci * 120762306a36Sopenharmony_ci * If lazy shootdown was improved to reduce IPIs (e.g., 120862306a36Sopenharmony_ci * by batching), then it may end up being better to use 120962306a36Sopenharmony_ci * tlbies here instead. 121062306a36Sopenharmony_ci */ 121162306a36Sopenharmony_ci preempt_disable(); 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_ci smp_mb(); /* see radix__flush_tlb_mm */ 121462306a36Sopenharmony_ci exit_flush_lazy_tlbs(mm); 121562306a36Sopenharmony_ci __flush_all_mm(mm, true); 121662306a36Sopenharmony_ci 121762306a36Sopenharmony_ci preempt_enable(); 121862306a36Sopenharmony_ci } else { 121962306a36Sopenharmony_ci __flush_all_mm(mm, true); 122062306a36Sopenharmony_ci } 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_ci } else if ( (psize = radix_get_mmu_psize(page_size)) == -1) { 122362306a36Sopenharmony_ci if (!tlb->freed_tables) 122462306a36Sopenharmony_ci radix__flush_tlb_mm(mm); 122562306a36Sopenharmony_ci else 122662306a36Sopenharmony_ci radix__flush_all_mm(mm); 122762306a36Sopenharmony_ci } else { 122862306a36Sopenharmony_ci if (!tlb->freed_tables) 122962306a36Sopenharmony_ci radix__flush_tlb_range_psize(mm, start, end, psize); 123062306a36Sopenharmony_ci else 123162306a36Sopenharmony_ci radix__flush_tlb_pwc_range_psize(mm, start, end, psize); 123262306a36Sopenharmony_ci } 123362306a36Sopenharmony_ci} 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_cistatic void __radix__flush_tlb_range_psize(struct mm_struct *mm, 123662306a36Sopenharmony_ci unsigned long start, unsigned long end, 123762306a36Sopenharmony_ci int psize, bool also_pwc) 123862306a36Sopenharmony_ci{ 123962306a36Sopenharmony_ci unsigned long pid; 124062306a36Sopenharmony_ci unsigned int page_shift = mmu_psize_defs[psize].shift; 124162306a36Sopenharmony_ci unsigned long page_size = 1UL << page_shift; 124262306a36Sopenharmony_ci unsigned long nr_pages = (end - start) >> page_shift; 124362306a36Sopenharmony_ci bool flush_pid; 124462306a36Sopenharmony_ci enum tlb_flush_type type; 124562306a36Sopenharmony_ci 124662306a36Sopenharmony_ci pid = mm->context.id; 124762306a36Sopenharmony_ci if (WARN_ON_ONCE(pid == MMU_NO_CONTEXT)) 124862306a36Sopenharmony_ci return; 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_ci WARN_ON_ONCE(end == TLB_FLUSH_ALL); 125162306a36Sopenharmony_ci 125262306a36Sopenharmony_ci preempt_disable(); 125362306a36Sopenharmony_ci smp_mb(); /* see radix__flush_tlb_mm */ 125462306a36Sopenharmony_ci type = flush_type_needed(mm, false); 125562306a36Sopenharmony_ci if (type == FLUSH_TYPE_NONE) 125662306a36Sopenharmony_ci goto out; 125762306a36Sopenharmony_ci 125862306a36Sopenharmony_ci if (type == FLUSH_TYPE_GLOBAL) 125962306a36Sopenharmony_ci flush_pid = nr_pages > tlb_single_page_flush_ceiling; 126062306a36Sopenharmony_ci else 126162306a36Sopenharmony_ci flush_pid = nr_pages > tlb_local_single_page_flush_ceiling; 126262306a36Sopenharmony_ci 126362306a36Sopenharmony_ci if (!mmu_has_feature(MMU_FTR_GTSE) && type == FLUSH_TYPE_GLOBAL) { 126462306a36Sopenharmony_ci unsigned long tgt = H_RPTI_TARGET_CMMU; 126562306a36Sopenharmony_ci unsigned long type = H_RPTI_TYPE_TLB; 126662306a36Sopenharmony_ci unsigned long pg_sizes = psize_to_rpti_pgsize(psize); 126762306a36Sopenharmony_ci 126862306a36Sopenharmony_ci if (also_pwc) 126962306a36Sopenharmony_ci type |= H_RPTI_TYPE_PWC; 127062306a36Sopenharmony_ci if (atomic_read(&mm->context.copros) > 0) 127162306a36Sopenharmony_ci tgt |= H_RPTI_TARGET_NMMU; 127262306a36Sopenharmony_ci pseries_rpt_invalidate(pid, tgt, type, pg_sizes, start, end); 127362306a36Sopenharmony_ci } else if (flush_pid) { 127462306a36Sopenharmony_ci if (type == FLUSH_TYPE_LOCAL) { 127562306a36Sopenharmony_ci _tlbiel_pid(pid, also_pwc ? RIC_FLUSH_ALL : RIC_FLUSH_TLB); 127662306a36Sopenharmony_ci } else { 127762306a36Sopenharmony_ci if (cputlb_use_tlbie()) { 127862306a36Sopenharmony_ci if (mm_needs_flush_escalation(mm)) 127962306a36Sopenharmony_ci also_pwc = true; 128062306a36Sopenharmony_ci 128162306a36Sopenharmony_ci _tlbie_pid(pid, 128262306a36Sopenharmony_ci also_pwc ? RIC_FLUSH_ALL : RIC_FLUSH_TLB); 128362306a36Sopenharmony_ci } else { 128462306a36Sopenharmony_ci _tlbiel_pid_multicast(mm, pid, 128562306a36Sopenharmony_ci also_pwc ? RIC_FLUSH_ALL : RIC_FLUSH_TLB); 128662306a36Sopenharmony_ci } 128762306a36Sopenharmony_ci 128862306a36Sopenharmony_ci } 128962306a36Sopenharmony_ci } else { 129062306a36Sopenharmony_ci if (type == FLUSH_TYPE_LOCAL) 129162306a36Sopenharmony_ci _tlbiel_va_range(start, end, pid, page_size, psize, also_pwc); 129262306a36Sopenharmony_ci else if (cputlb_use_tlbie()) 129362306a36Sopenharmony_ci _tlbie_va_range(start, end, pid, page_size, psize, also_pwc); 129462306a36Sopenharmony_ci else 129562306a36Sopenharmony_ci _tlbiel_va_range_multicast(mm, 129662306a36Sopenharmony_ci start, end, pid, page_size, psize, also_pwc); 129762306a36Sopenharmony_ci } 129862306a36Sopenharmony_ciout: 129962306a36Sopenharmony_ci preempt_enable(); 130062306a36Sopenharmony_ci mmu_notifier_arch_invalidate_secondary_tlbs(mm, start, end); 130162306a36Sopenharmony_ci} 130262306a36Sopenharmony_ci 130362306a36Sopenharmony_civoid radix__flush_tlb_range_psize(struct mm_struct *mm, unsigned long start, 130462306a36Sopenharmony_ci unsigned long end, int psize) 130562306a36Sopenharmony_ci{ 130662306a36Sopenharmony_ci return __radix__flush_tlb_range_psize(mm, start, end, psize, false); 130762306a36Sopenharmony_ci} 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_civoid radix__flush_tlb_pwc_range_psize(struct mm_struct *mm, unsigned long start, 131062306a36Sopenharmony_ci unsigned long end, int psize) 131162306a36Sopenharmony_ci{ 131262306a36Sopenharmony_ci __radix__flush_tlb_range_psize(mm, start, end, psize, true); 131362306a36Sopenharmony_ci} 131462306a36Sopenharmony_ci 131562306a36Sopenharmony_ci#ifdef CONFIG_TRANSPARENT_HUGEPAGE 131662306a36Sopenharmony_civoid radix__flush_tlb_collapsed_pmd(struct mm_struct *mm, unsigned long addr) 131762306a36Sopenharmony_ci{ 131862306a36Sopenharmony_ci unsigned long pid, end; 131962306a36Sopenharmony_ci enum tlb_flush_type type; 132062306a36Sopenharmony_ci 132162306a36Sopenharmony_ci pid = mm->context.id; 132262306a36Sopenharmony_ci if (WARN_ON_ONCE(pid == MMU_NO_CONTEXT)) 132362306a36Sopenharmony_ci return; 132462306a36Sopenharmony_ci 132562306a36Sopenharmony_ci /* 4k page size, just blow the world */ 132662306a36Sopenharmony_ci if (PAGE_SIZE == 0x1000) { 132762306a36Sopenharmony_ci radix__flush_all_mm(mm); 132862306a36Sopenharmony_ci return; 132962306a36Sopenharmony_ci } 133062306a36Sopenharmony_ci 133162306a36Sopenharmony_ci end = addr + HPAGE_PMD_SIZE; 133262306a36Sopenharmony_ci 133362306a36Sopenharmony_ci /* Otherwise first do the PWC, then iterate the pages. */ 133462306a36Sopenharmony_ci preempt_disable(); 133562306a36Sopenharmony_ci smp_mb(); /* see radix__flush_tlb_mm */ 133662306a36Sopenharmony_ci type = flush_type_needed(mm, false); 133762306a36Sopenharmony_ci if (type == FLUSH_TYPE_LOCAL) { 133862306a36Sopenharmony_ci _tlbiel_va_range(addr, end, pid, PAGE_SIZE, mmu_virtual_psize, true); 133962306a36Sopenharmony_ci } else if (type == FLUSH_TYPE_GLOBAL) { 134062306a36Sopenharmony_ci if (!mmu_has_feature(MMU_FTR_GTSE)) { 134162306a36Sopenharmony_ci unsigned long tgt, type, pg_sizes; 134262306a36Sopenharmony_ci 134362306a36Sopenharmony_ci tgt = H_RPTI_TARGET_CMMU; 134462306a36Sopenharmony_ci type = H_RPTI_TYPE_TLB | H_RPTI_TYPE_PWC | 134562306a36Sopenharmony_ci H_RPTI_TYPE_PRT; 134662306a36Sopenharmony_ci pg_sizes = psize_to_rpti_pgsize(mmu_virtual_psize); 134762306a36Sopenharmony_ci 134862306a36Sopenharmony_ci if (atomic_read(&mm->context.copros) > 0) 134962306a36Sopenharmony_ci tgt |= H_RPTI_TARGET_NMMU; 135062306a36Sopenharmony_ci pseries_rpt_invalidate(pid, tgt, type, pg_sizes, 135162306a36Sopenharmony_ci addr, end); 135262306a36Sopenharmony_ci } else if (cputlb_use_tlbie()) 135362306a36Sopenharmony_ci _tlbie_va_range(addr, end, pid, PAGE_SIZE, mmu_virtual_psize, true); 135462306a36Sopenharmony_ci else 135562306a36Sopenharmony_ci _tlbiel_va_range_multicast(mm, 135662306a36Sopenharmony_ci addr, end, pid, PAGE_SIZE, mmu_virtual_psize, true); 135762306a36Sopenharmony_ci } 135862306a36Sopenharmony_ci 135962306a36Sopenharmony_ci preempt_enable(); 136062306a36Sopenharmony_ci} 136162306a36Sopenharmony_ci#endif /* CONFIG_TRANSPARENT_HUGEPAGE */ 136262306a36Sopenharmony_ci 136362306a36Sopenharmony_civoid radix__flush_pmd_tlb_range(struct vm_area_struct *vma, 136462306a36Sopenharmony_ci unsigned long start, unsigned long end) 136562306a36Sopenharmony_ci{ 136662306a36Sopenharmony_ci radix__flush_tlb_range_psize(vma->vm_mm, start, end, MMU_PAGE_2M); 136762306a36Sopenharmony_ci} 136862306a36Sopenharmony_ciEXPORT_SYMBOL(radix__flush_pmd_tlb_range); 136962306a36Sopenharmony_ci 137062306a36Sopenharmony_civoid radix__flush_pud_tlb_range(struct vm_area_struct *vma, 137162306a36Sopenharmony_ci unsigned long start, unsigned long end) 137262306a36Sopenharmony_ci{ 137362306a36Sopenharmony_ci radix__flush_tlb_range_psize(vma->vm_mm, start, end, MMU_PAGE_1G); 137462306a36Sopenharmony_ci} 137562306a36Sopenharmony_ciEXPORT_SYMBOL(radix__flush_pud_tlb_range); 137662306a36Sopenharmony_ci 137762306a36Sopenharmony_civoid radix__flush_tlb_all(void) 137862306a36Sopenharmony_ci{ 137962306a36Sopenharmony_ci unsigned long rb,prs,r,rs; 138062306a36Sopenharmony_ci unsigned long ric = RIC_FLUSH_ALL; 138162306a36Sopenharmony_ci 138262306a36Sopenharmony_ci rb = 0x3 << PPC_BITLSHIFT(53); /* IS = 3 */ 138362306a36Sopenharmony_ci prs = 0; /* partition scoped */ 138462306a36Sopenharmony_ci r = 1; /* radix format */ 138562306a36Sopenharmony_ci rs = 1 & ((1UL << 32) - 1); /* any LPID value to flush guest mappings */ 138662306a36Sopenharmony_ci 138762306a36Sopenharmony_ci asm volatile("ptesync": : :"memory"); 138862306a36Sopenharmony_ci /* 138962306a36Sopenharmony_ci * now flush guest entries by passing PRS = 1 and LPID != 0 139062306a36Sopenharmony_ci */ 139162306a36Sopenharmony_ci asm volatile(PPC_TLBIE_5(%0, %4, %3, %2, %1) 139262306a36Sopenharmony_ci : : "r"(rb), "i"(r), "i"(1), "i"(ric), "r"(rs) : "memory"); 139362306a36Sopenharmony_ci /* 139462306a36Sopenharmony_ci * now flush host entires by passing PRS = 0 and LPID == 0 139562306a36Sopenharmony_ci */ 139662306a36Sopenharmony_ci asm volatile(PPC_TLBIE_5(%0, %4, %3, %2, %1) 139762306a36Sopenharmony_ci : : "r"(rb), "i"(r), "i"(prs), "i"(ric), "r"(0) : "memory"); 139862306a36Sopenharmony_ci asm volatile("eieio; tlbsync; ptesync": : :"memory"); 139962306a36Sopenharmony_ci} 140062306a36Sopenharmony_ci 140162306a36Sopenharmony_ci#ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE 140262306a36Sopenharmony_cistatic __always_inline void __tlbie_pid_lpid(unsigned long pid, 140362306a36Sopenharmony_ci unsigned long lpid, 140462306a36Sopenharmony_ci unsigned long ric) 140562306a36Sopenharmony_ci{ 140662306a36Sopenharmony_ci unsigned long rb, rs, prs, r; 140762306a36Sopenharmony_ci 140862306a36Sopenharmony_ci rb = PPC_BIT(53); /* IS = 1 */ 140962306a36Sopenharmony_ci rs = (pid << PPC_BITLSHIFT(31)) | (lpid & ~(PPC_BITMASK(0, 31))); 141062306a36Sopenharmony_ci prs = 1; /* process scoped */ 141162306a36Sopenharmony_ci r = 1; /* radix format */ 141262306a36Sopenharmony_ci 141362306a36Sopenharmony_ci asm volatile(PPC_TLBIE_5(%0, %4, %3, %2, %1) 141462306a36Sopenharmony_ci : : "r"(rb), "i"(r), "i"(prs), "i"(ric), "r"(rs) : "memory"); 141562306a36Sopenharmony_ci trace_tlbie(0, 0, rb, rs, ric, prs, r); 141662306a36Sopenharmony_ci} 141762306a36Sopenharmony_ci 141862306a36Sopenharmony_cistatic __always_inline void __tlbie_va_lpid(unsigned long va, unsigned long pid, 141962306a36Sopenharmony_ci unsigned long lpid, 142062306a36Sopenharmony_ci unsigned long ap, unsigned long ric) 142162306a36Sopenharmony_ci{ 142262306a36Sopenharmony_ci unsigned long rb, rs, prs, r; 142362306a36Sopenharmony_ci 142462306a36Sopenharmony_ci rb = va & ~(PPC_BITMASK(52, 63)); 142562306a36Sopenharmony_ci rb |= ap << PPC_BITLSHIFT(58); 142662306a36Sopenharmony_ci rs = (pid << PPC_BITLSHIFT(31)) | (lpid & ~(PPC_BITMASK(0, 31))); 142762306a36Sopenharmony_ci prs = 1; /* process scoped */ 142862306a36Sopenharmony_ci r = 1; /* radix format */ 142962306a36Sopenharmony_ci 143062306a36Sopenharmony_ci asm volatile(PPC_TLBIE_5(%0, %4, %3, %2, %1) 143162306a36Sopenharmony_ci : : "r"(rb), "i"(r), "i"(prs), "i"(ric), "r"(rs) : "memory"); 143262306a36Sopenharmony_ci trace_tlbie(0, 0, rb, rs, ric, prs, r); 143362306a36Sopenharmony_ci} 143462306a36Sopenharmony_ci 143562306a36Sopenharmony_cistatic inline void fixup_tlbie_pid_lpid(unsigned long pid, unsigned long lpid) 143662306a36Sopenharmony_ci{ 143762306a36Sopenharmony_ci /* 143862306a36Sopenharmony_ci * We can use any address for the invalidation, pick one which is 143962306a36Sopenharmony_ci * probably unused as an optimisation. 144062306a36Sopenharmony_ci */ 144162306a36Sopenharmony_ci unsigned long va = ((1UL << 52) - 1); 144262306a36Sopenharmony_ci 144362306a36Sopenharmony_ci if (cpu_has_feature(CPU_FTR_P9_TLBIE_ERAT_BUG)) { 144462306a36Sopenharmony_ci asm volatile("ptesync" : : : "memory"); 144562306a36Sopenharmony_ci __tlbie_pid_lpid(0, lpid, RIC_FLUSH_TLB); 144662306a36Sopenharmony_ci } 144762306a36Sopenharmony_ci 144862306a36Sopenharmony_ci if (cpu_has_feature(CPU_FTR_P9_TLBIE_STQ_BUG)) { 144962306a36Sopenharmony_ci asm volatile("ptesync" : : : "memory"); 145062306a36Sopenharmony_ci __tlbie_va_lpid(va, pid, lpid, mmu_get_ap(MMU_PAGE_64K), 145162306a36Sopenharmony_ci RIC_FLUSH_TLB); 145262306a36Sopenharmony_ci } 145362306a36Sopenharmony_ci} 145462306a36Sopenharmony_ci 145562306a36Sopenharmony_cistatic inline void _tlbie_pid_lpid(unsigned long pid, unsigned long lpid, 145662306a36Sopenharmony_ci unsigned long ric) 145762306a36Sopenharmony_ci{ 145862306a36Sopenharmony_ci asm volatile("ptesync" : : : "memory"); 145962306a36Sopenharmony_ci 146062306a36Sopenharmony_ci /* 146162306a36Sopenharmony_ci * Workaround the fact that the "ric" argument to __tlbie_pid 146262306a36Sopenharmony_ci * must be a compile-time contraint to match the "i" constraint 146362306a36Sopenharmony_ci * in the asm statement. 146462306a36Sopenharmony_ci */ 146562306a36Sopenharmony_ci switch (ric) { 146662306a36Sopenharmony_ci case RIC_FLUSH_TLB: 146762306a36Sopenharmony_ci __tlbie_pid_lpid(pid, lpid, RIC_FLUSH_TLB); 146862306a36Sopenharmony_ci fixup_tlbie_pid_lpid(pid, lpid); 146962306a36Sopenharmony_ci break; 147062306a36Sopenharmony_ci case RIC_FLUSH_PWC: 147162306a36Sopenharmony_ci __tlbie_pid_lpid(pid, lpid, RIC_FLUSH_PWC); 147262306a36Sopenharmony_ci break; 147362306a36Sopenharmony_ci case RIC_FLUSH_ALL: 147462306a36Sopenharmony_ci default: 147562306a36Sopenharmony_ci __tlbie_pid_lpid(pid, lpid, RIC_FLUSH_ALL); 147662306a36Sopenharmony_ci fixup_tlbie_pid_lpid(pid, lpid); 147762306a36Sopenharmony_ci } 147862306a36Sopenharmony_ci asm volatile("eieio; tlbsync; ptesync" : : : "memory"); 147962306a36Sopenharmony_ci} 148062306a36Sopenharmony_ci 148162306a36Sopenharmony_cistatic inline void fixup_tlbie_va_range_lpid(unsigned long va, 148262306a36Sopenharmony_ci unsigned long pid, 148362306a36Sopenharmony_ci unsigned long lpid, 148462306a36Sopenharmony_ci unsigned long ap) 148562306a36Sopenharmony_ci{ 148662306a36Sopenharmony_ci if (cpu_has_feature(CPU_FTR_P9_TLBIE_ERAT_BUG)) { 148762306a36Sopenharmony_ci asm volatile("ptesync" : : : "memory"); 148862306a36Sopenharmony_ci __tlbie_pid_lpid(0, lpid, RIC_FLUSH_TLB); 148962306a36Sopenharmony_ci } 149062306a36Sopenharmony_ci 149162306a36Sopenharmony_ci if (cpu_has_feature(CPU_FTR_P9_TLBIE_STQ_BUG)) { 149262306a36Sopenharmony_ci asm volatile("ptesync" : : : "memory"); 149362306a36Sopenharmony_ci __tlbie_va_lpid(va, pid, lpid, ap, RIC_FLUSH_TLB); 149462306a36Sopenharmony_ci } 149562306a36Sopenharmony_ci} 149662306a36Sopenharmony_ci 149762306a36Sopenharmony_cistatic inline void __tlbie_va_range_lpid(unsigned long start, unsigned long end, 149862306a36Sopenharmony_ci unsigned long pid, unsigned long lpid, 149962306a36Sopenharmony_ci unsigned long page_size, 150062306a36Sopenharmony_ci unsigned long psize) 150162306a36Sopenharmony_ci{ 150262306a36Sopenharmony_ci unsigned long addr; 150362306a36Sopenharmony_ci unsigned long ap = mmu_get_ap(psize); 150462306a36Sopenharmony_ci 150562306a36Sopenharmony_ci for (addr = start; addr < end; addr += page_size) 150662306a36Sopenharmony_ci __tlbie_va_lpid(addr, pid, lpid, ap, RIC_FLUSH_TLB); 150762306a36Sopenharmony_ci 150862306a36Sopenharmony_ci fixup_tlbie_va_range_lpid(addr - page_size, pid, lpid, ap); 150962306a36Sopenharmony_ci} 151062306a36Sopenharmony_ci 151162306a36Sopenharmony_cistatic inline void _tlbie_va_range_lpid(unsigned long start, unsigned long end, 151262306a36Sopenharmony_ci unsigned long pid, unsigned long lpid, 151362306a36Sopenharmony_ci unsigned long page_size, 151462306a36Sopenharmony_ci unsigned long psize, bool also_pwc) 151562306a36Sopenharmony_ci{ 151662306a36Sopenharmony_ci asm volatile("ptesync" : : : "memory"); 151762306a36Sopenharmony_ci if (also_pwc) 151862306a36Sopenharmony_ci __tlbie_pid_lpid(pid, lpid, RIC_FLUSH_PWC); 151962306a36Sopenharmony_ci __tlbie_va_range_lpid(start, end, pid, lpid, page_size, psize); 152062306a36Sopenharmony_ci asm volatile("eieio; tlbsync; ptesync" : : : "memory"); 152162306a36Sopenharmony_ci} 152262306a36Sopenharmony_ci 152362306a36Sopenharmony_ci/* 152462306a36Sopenharmony_ci * Performs process-scoped invalidations for a given LPID 152562306a36Sopenharmony_ci * as part of H_RPT_INVALIDATE hcall. 152662306a36Sopenharmony_ci */ 152762306a36Sopenharmony_civoid do_h_rpt_invalidate_prt(unsigned long pid, unsigned long lpid, 152862306a36Sopenharmony_ci unsigned long type, unsigned long pg_sizes, 152962306a36Sopenharmony_ci unsigned long start, unsigned long end) 153062306a36Sopenharmony_ci{ 153162306a36Sopenharmony_ci unsigned long psize, nr_pages; 153262306a36Sopenharmony_ci struct mmu_psize_def *def; 153362306a36Sopenharmony_ci bool flush_pid; 153462306a36Sopenharmony_ci 153562306a36Sopenharmony_ci /* 153662306a36Sopenharmony_ci * A H_RPTI_TYPE_ALL request implies RIC=3, hence 153762306a36Sopenharmony_ci * do a single IS=1 based flush. 153862306a36Sopenharmony_ci */ 153962306a36Sopenharmony_ci if ((type & H_RPTI_TYPE_ALL) == H_RPTI_TYPE_ALL) { 154062306a36Sopenharmony_ci _tlbie_pid_lpid(pid, lpid, RIC_FLUSH_ALL); 154162306a36Sopenharmony_ci return; 154262306a36Sopenharmony_ci } 154362306a36Sopenharmony_ci 154462306a36Sopenharmony_ci if (type & H_RPTI_TYPE_PWC) 154562306a36Sopenharmony_ci _tlbie_pid_lpid(pid, lpid, RIC_FLUSH_PWC); 154662306a36Sopenharmony_ci 154762306a36Sopenharmony_ci /* Full PID flush */ 154862306a36Sopenharmony_ci if (start == 0 && end == -1) 154962306a36Sopenharmony_ci return _tlbie_pid_lpid(pid, lpid, RIC_FLUSH_TLB); 155062306a36Sopenharmony_ci 155162306a36Sopenharmony_ci /* Do range invalidation for all the valid page sizes */ 155262306a36Sopenharmony_ci for (psize = 0; psize < MMU_PAGE_COUNT; psize++) { 155362306a36Sopenharmony_ci def = &mmu_psize_defs[psize]; 155462306a36Sopenharmony_ci if (!(pg_sizes & def->h_rpt_pgsize)) 155562306a36Sopenharmony_ci continue; 155662306a36Sopenharmony_ci 155762306a36Sopenharmony_ci nr_pages = (end - start) >> def->shift; 155862306a36Sopenharmony_ci flush_pid = nr_pages > tlb_single_page_flush_ceiling; 155962306a36Sopenharmony_ci 156062306a36Sopenharmony_ci /* 156162306a36Sopenharmony_ci * If the number of pages spanning the range is above 156262306a36Sopenharmony_ci * the ceiling, convert the request into a full PID flush. 156362306a36Sopenharmony_ci * And since PID flush takes out all the page sizes, there 156462306a36Sopenharmony_ci * is no need to consider remaining page sizes. 156562306a36Sopenharmony_ci */ 156662306a36Sopenharmony_ci if (flush_pid) { 156762306a36Sopenharmony_ci _tlbie_pid_lpid(pid, lpid, RIC_FLUSH_TLB); 156862306a36Sopenharmony_ci return; 156962306a36Sopenharmony_ci } 157062306a36Sopenharmony_ci _tlbie_va_range_lpid(start, end, pid, lpid, 157162306a36Sopenharmony_ci (1UL << def->shift), psize, false); 157262306a36Sopenharmony_ci } 157362306a36Sopenharmony_ci} 157462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(do_h_rpt_invalidate_prt); 157562306a36Sopenharmony_ci 157662306a36Sopenharmony_ci#endif /* CONFIG_KVM_BOOK3S_HV_POSSIBLE */ 157762306a36Sopenharmony_ci 157862306a36Sopenharmony_cistatic int __init create_tlb_single_page_flush_ceiling(void) 157962306a36Sopenharmony_ci{ 158062306a36Sopenharmony_ci debugfs_create_u32("tlb_single_page_flush_ceiling", 0600, 158162306a36Sopenharmony_ci arch_debugfs_dir, &tlb_single_page_flush_ceiling); 158262306a36Sopenharmony_ci debugfs_create_u32("tlb_local_single_page_flush_ceiling", 0600, 158362306a36Sopenharmony_ci arch_debugfs_dir, &tlb_local_single_page_flush_ceiling); 158462306a36Sopenharmony_ci return 0; 158562306a36Sopenharmony_ci} 158662306a36Sopenharmony_cilate_initcall(create_tlb_single_page_flush_ceiling); 158762306a36Sopenharmony_ci 1588