18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * TLB support routines. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 1998-2001, 2003 Hewlett-Packard Co 68c2ecf20Sopenharmony_ci * David Mosberger-Tang <davidm@hpl.hp.com> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * 08/02/00 A. Mallick <asit.k.mallick@intel.com> 98c2ecf20Sopenharmony_ci * Modified RID allocation for SMP 108c2ecf20Sopenharmony_ci * Goutham Rao <goutham.rao@intel.com> 118c2ecf20Sopenharmony_ci * IPI based ptc implementation and A-step IPI implementation. 128c2ecf20Sopenharmony_ci * Rohit Seth <rohit.seth@intel.com> 138c2ecf20Sopenharmony_ci * Ken Chen <kenneth.w.chen@intel.com> 148c2ecf20Sopenharmony_ci * Christophe de Dinechin <ddd@hp.com>: Avoid ptc.e on memory allocation 158c2ecf20Sopenharmony_ci * Copyright (C) 2007 Intel Corp 168c2ecf20Sopenharmony_ci * Fenghua Yu <fenghua.yu@intel.com> 178c2ecf20Sopenharmony_ci * Add multiple ptc.g/ptc.ga instruction support in global tlb purge. 188c2ecf20Sopenharmony_ci */ 198c2ecf20Sopenharmony_ci#include <linux/module.h> 208c2ecf20Sopenharmony_ci#include <linux/init.h> 218c2ecf20Sopenharmony_ci#include <linux/kernel.h> 228c2ecf20Sopenharmony_ci#include <linux/sched.h> 238c2ecf20Sopenharmony_ci#include <linux/smp.h> 248c2ecf20Sopenharmony_ci#include <linux/mm.h> 258c2ecf20Sopenharmony_ci#include <linux/memblock.h> 268c2ecf20Sopenharmony_ci#include <linux/slab.h> 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#include <asm/delay.h> 298c2ecf20Sopenharmony_ci#include <asm/mmu_context.h> 308c2ecf20Sopenharmony_ci#include <asm/pal.h> 318c2ecf20Sopenharmony_ci#include <asm/tlbflush.h> 328c2ecf20Sopenharmony_ci#include <asm/dma.h> 338c2ecf20Sopenharmony_ci#include <asm/processor.h> 348c2ecf20Sopenharmony_ci#include <asm/sal.h> 358c2ecf20Sopenharmony_ci#include <asm/tlb.h> 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic struct { 388c2ecf20Sopenharmony_ci u64 mask; /* mask of supported purge page-sizes */ 398c2ecf20Sopenharmony_ci unsigned long max_bits; /* log2 of largest supported purge page-size */ 408c2ecf20Sopenharmony_ci} purge; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistruct ia64_ctx ia64_ctx = { 438c2ecf20Sopenharmony_ci .lock = __SPIN_LOCK_UNLOCKED(ia64_ctx.lock), 448c2ecf20Sopenharmony_ci .next = 1, 458c2ecf20Sopenharmony_ci .max_ctx = ~0U 468c2ecf20Sopenharmony_ci}; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ciDEFINE_PER_CPU(u8, ia64_need_tlb_flush); 498c2ecf20Sopenharmony_ciDEFINE_PER_CPU(u8, ia64_tr_num); /*Number of TR slots in current processor*/ 508c2ecf20Sopenharmony_ciDEFINE_PER_CPU(u8, ia64_tr_used); /*Max Slot number used by kernel*/ 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistruct ia64_tr_entry *ia64_idtrs[NR_CPUS]; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci/* 558c2ecf20Sopenharmony_ci * Initializes the ia64_ctx.bitmap array based on max_ctx+1. 568c2ecf20Sopenharmony_ci * Called after cpu_init() has setup ia64_ctx.max_ctx based on 578c2ecf20Sopenharmony_ci * maximum RID that is supported by boot CPU. 588c2ecf20Sopenharmony_ci */ 598c2ecf20Sopenharmony_civoid __init 608c2ecf20Sopenharmony_cimmu_context_init (void) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci ia64_ctx.bitmap = memblock_alloc((ia64_ctx.max_ctx + 1) >> 3, 638c2ecf20Sopenharmony_ci SMP_CACHE_BYTES); 648c2ecf20Sopenharmony_ci if (!ia64_ctx.bitmap) 658c2ecf20Sopenharmony_ci panic("%s: Failed to allocate %u bytes\n", __func__, 668c2ecf20Sopenharmony_ci (ia64_ctx.max_ctx + 1) >> 3); 678c2ecf20Sopenharmony_ci ia64_ctx.flushmap = memblock_alloc((ia64_ctx.max_ctx + 1) >> 3, 688c2ecf20Sopenharmony_ci SMP_CACHE_BYTES); 698c2ecf20Sopenharmony_ci if (!ia64_ctx.flushmap) 708c2ecf20Sopenharmony_ci panic("%s: Failed to allocate %u bytes\n", __func__, 718c2ecf20Sopenharmony_ci (ia64_ctx.max_ctx + 1) >> 3); 728c2ecf20Sopenharmony_ci} 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci/* 758c2ecf20Sopenharmony_ci * Acquire the ia64_ctx.lock before calling this function! 768c2ecf20Sopenharmony_ci */ 778c2ecf20Sopenharmony_civoid 788c2ecf20Sopenharmony_ciwrap_mmu_context (struct mm_struct *mm) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci int i, cpu; 818c2ecf20Sopenharmony_ci unsigned long flush_bit; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci for (i=0; i <= ia64_ctx.max_ctx / BITS_PER_LONG; i++) { 848c2ecf20Sopenharmony_ci flush_bit = xchg(&ia64_ctx.flushmap[i], 0); 858c2ecf20Sopenharmony_ci ia64_ctx.bitmap[i] ^= flush_bit; 868c2ecf20Sopenharmony_ci } 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci /* use offset at 300 to skip daemons */ 898c2ecf20Sopenharmony_ci ia64_ctx.next = find_next_zero_bit(ia64_ctx.bitmap, 908c2ecf20Sopenharmony_ci ia64_ctx.max_ctx, 300); 918c2ecf20Sopenharmony_ci ia64_ctx.limit = find_next_bit(ia64_ctx.bitmap, 928c2ecf20Sopenharmony_ci ia64_ctx.max_ctx, ia64_ctx.next); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci /* 958c2ecf20Sopenharmony_ci * can't call flush_tlb_all() here because of race condition 968c2ecf20Sopenharmony_ci * with O(1) scheduler [EF] 978c2ecf20Sopenharmony_ci */ 988c2ecf20Sopenharmony_ci cpu = get_cpu(); /* prevent preemption/migration */ 998c2ecf20Sopenharmony_ci for_each_online_cpu(i) 1008c2ecf20Sopenharmony_ci if (i != cpu) 1018c2ecf20Sopenharmony_ci per_cpu(ia64_need_tlb_flush, i) = 1; 1028c2ecf20Sopenharmony_ci put_cpu(); 1038c2ecf20Sopenharmony_ci local_flush_tlb_all(); 1048c2ecf20Sopenharmony_ci} 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci/* 1078c2ecf20Sopenharmony_ci * Implement "spinaphores" ... like counting semaphores, but they 1088c2ecf20Sopenharmony_ci * spin instead of sleeping. If there are ever any other users for 1098c2ecf20Sopenharmony_ci * this primitive it can be moved up to a spinaphore.h header. 1108c2ecf20Sopenharmony_ci */ 1118c2ecf20Sopenharmony_cistruct spinaphore { 1128c2ecf20Sopenharmony_ci unsigned long ticket; 1138c2ecf20Sopenharmony_ci unsigned long serve; 1148c2ecf20Sopenharmony_ci}; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistatic inline void spinaphore_init(struct spinaphore *ss, int val) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci ss->ticket = 0; 1198c2ecf20Sopenharmony_ci ss->serve = val; 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistatic inline void down_spin(struct spinaphore *ss) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci unsigned long t = ia64_fetchadd(1, &ss->ticket, acq), serve; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci if (time_before(t, ss->serve)) 1278c2ecf20Sopenharmony_ci return; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci ia64_invala(); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci for (;;) { 1328c2ecf20Sopenharmony_ci asm volatile ("ld8.c.nc %0=[%1]" : "=r"(serve) : "r"(&ss->serve) : "memory"); 1338c2ecf20Sopenharmony_ci if (time_before(t, serve)) 1348c2ecf20Sopenharmony_ci return; 1358c2ecf20Sopenharmony_ci cpu_relax(); 1368c2ecf20Sopenharmony_ci } 1378c2ecf20Sopenharmony_ci} 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_cistatic inline void up_spin(struct spinaphore *ss) 1408c2ecf20Sopenharmony_ci{ 1418c2ecf20Sopenharmony_ci ia64_fetchadd(1, &ss->serve, rel); 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_cistatic struct spinaphore ptcg_sem; 1458c2ecf20Sopenharmony_cistatic u16 nptcg = 1; 1468c2ecf20Sopenharmony_cistatic int need_ptcg_sem = 1; 1478c2ecf20Sopenharmony_cistatic int toolatetochangeptcgsem = 0; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci/* 1508c2ecf20Sopenharmony_ci * Kernel parameter "nptcg=" overrides max number of concurrent global TLB 1518c2ecf20Sopenharmony_ci * purges which is reported from either PAL or SAL PALO. 1528c2ecf20Sopenharmony_ci * 1538c2ecf20Sopenharmony_ci * We don't have sanity checking for nptcg value. It's the user's responsibility 1548c2ecf20Sopenharmony_ci * for valid nptcg value on the platform. Otherwise, kernel may hang in some 1558c2ecf20Sopenharmony_ci * cases. 1568c2ecf20Sopenharmony_ci */ 1578c2ecf20Sopenharmony_cistatic int __init 1588c2ecf20Sopenharmony_ciset_nptcg(char *str) 1598c2ecf20Sopenharmony_ci{ 1608c2ecf20Sopenharmony_ci int value = 0; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci get_option(&str, &value); 1638c2ecf20Sopenharmony_ci setup_ptcg_sem(value, NPTCG_FROM_KERNEL_PARAMETER); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci return 1; 1668c2ecf20Sopenharmony_ci} 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci__setup("nptcg=", set_nptcg); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci/* 1718c2ecf20Sopenharmony_ci * Maximum number of simultaneous ptc.g purges in the system can 1728c2ecf20Sopenharmony_ci * be defined by PAL_VM_SUMMARY (in which case we should take 1738c2ecf20Sopenharmony_ci * the smallest value for any cpu in the system) or by the PAL 1748c2ecf20Sopenharmony_ci * override table (in which case we should ignore the value from 1758c2ecf20Sopenharmony_ci * PAL_VM_SUMMARY). 1768c2ecf20Sopenharmony_ci * 1778c2ecf20Sopenharmony_ci * Kernel parameter "nptcg=" overrides maximum number of simultanesous ptc.g 1788c2ecf20Sopenharmony_ci * purges defined in either PAL_VM_SUMMARY or PAL override table. In this case, 1798c2ecf20Sopenharmony_ci * we should ignore the value from either PAL_VM_SUMMARY or PAL override table. 1808c2ecf20Sopenharmony_ci * 1818c2ecf20Sopenharmony_ci * Complicating the logic here is the fact that num_possible_cpus() 1828c2ecf20Sopenharmony_ci * isn't fully setup until we start bringing cpus online. 1838c2ecf20Sopenharmony_ci */ 1848c2ecf20Sopenharmony_civoid 1858c2ecf20Sopenharmony_cisetup_ptcg_sem(int max_purges, int nptcg_from) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci static int kp_override; 1888c2ecf20Sopenharmony_ci static int palo_override; 1898c2ecf20Sopenharmony_ci static int firstcpu = 1; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci if (toolatetochangeptcgsem) { 1928c2ecf20Sopenharmony_ci if (nptcg_from == NPTCG_FROM_PAL && max_purges == 0) 1938c2ecf20Sopenharmony_ci BUG_ON(1 < nptcg); 1948c2ecf20Sopenharmony_ci else 1958c2ecf20Sopenharmony_ci BUG_ON(max_purges < nptcg); 1968c2ecf20Sopenharmony_ci return; 1978c2ecf20Sopenharmony_ci } 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci if (nptcg_from == NPTCG_FROM_KERNEL_PARAMETER) { 2008c2ecf20Sopenharmony_ci kp_override = 1; 2018c2ecf20Sopenharmony_ci nptcg = max_purges; 2028c2ecf20Sopenharmony_ci goto resetsema; 2038c2ecf20Sopenharmony_ci } 2048c2ecf20Sopenharmony_ci if (kp_override) { 2058c2ecf20Sopenharmony_ci need_ptcg_sem = num_possible_cpus() > nptcg; 2068c2ecf20Sopenharmony_ci return; 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci if (nptcg_from == NPTCG_FROM_PALO) { 2108c2ecf20Sopenharmony_ci palo_override = 1; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci /* In PALO max_purges == 0 really means it! */ 2138c2ecf20Sopenharmony_ci if (max_purges == 0) 2148c2ecf20Sopenharmony_ci panic("Whoa! Platform does not support global TLB purges.\n"); 2158c2ecf20Sopenharmony_ci nptcg = max_purges; 2168c2ecf20Sopenharmony_ci if (nptcg == PALO_MAX_TLB_PURGES) { 2178c2ecf20Sopenharmony_ci need_ptcg_sem = 0; 2188c2ecf20Sopenharmony_ci return; 2198c2ecf20Sopenharmony_ci } 2208c2ecf20Sopenharmony_ci goto resetsema; 2218c2ecf20Sopenharmony_ci } 2228c2ecf20Sopenharmony_ci if (palo_override) { 2238c2ecf20Sopenharmony_ci if (nptcg != PALO_MAX_TLB_PURGES) 2248c2ecf20Sopenharmony_ci need_ptcg_sem = (num_possible_cpus() > nptcg); 2258c2ecf20Sopenharmony_ci return; 2268c2ecf20Sopenharmony_ci } 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci /* In PAL_VM_SUMMARY max_purges == 0 actually means 1 */ 2298c2ecf20Sopenharmony_ci if (max_purges == 0) max_purges = 1; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci if (firstcpu) { 2328c2ecf20Sopenharmony_ci nptcg = max_purges; 2338c2ecf20Sopenharmony_ci firstcpu = 0; 2348c2ecf20Sopenharmony_ci } 2358c2ecf20Sopenharmony_ci if (max_purges < nptcg) 2368c2ecf20Sopenharmony_ci nptcg = max_purges; 2378c2ecf20Sopenharmony_ci if (nptcg == PAL_MAX_PURGES) { 2388c2ecf20Sopenharmony_ci need_ptcg_sem = 0; 2398c2ecf20Sopenharmony_ci return; 2408c2ecf20Sopenharmony_ci } else 2418c2ecf20Sopenharmony_ci need_ptcg_sem = (num_possible_cpus() > nptcg); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ciresetsema: 2448c2ecf20Sopenharmony_ci spinaphore_init(&ptcg_sem, max_purges); 2458c2ecf20Sopenharmony_ci} 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci#ifdef CONFIG_SMP 2488c2ecf20Sopenharmony_cistatic void 2498c2ecf20Sopenharmony_ciia64_global_tlb_purge (struct mm_struct *mm, unsigned long start, 2508c2ecf20Sopenharmony_ci unsigned long end, unsigned long nbits) 2518c2ecf20Sopenharmony_ci{ 2528c2ecf20Sopenharmony_ci struct mm_struct *active_mm = current->active_mm; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci toolatetochangeptcgsem = 1; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci if (mm != active_mm) { 2578c2ecf20Sopenharmony_ci /* Restore region IDs for mm */ 2588c2ecf20Sopenharmony_ci if (mm && active_mm) { 2598c2ecf20Sopenharmony_ci activate_context(mm); 2608c2ecf20Sopenharmony_ci } else { 2618c2ecf20Sopenharmony_ci flush_tlb_all(); 2628c2ecf20Sopenharmony_ci return; 2638c2ecf20Sopenharmony_ci } 2648c2ecf20Sopenharmony_ci } 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci if (need_ptcg_sem) 2678c2ecf20Sopenharmony_ci down_spin(&ptcg_sem); 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci do { 2708c2ecf20Sopenharmony_ci /* 2718c2ecf20Sopenharmony_ci * Flush ALAT entries also. 2728c2ecf20Sopenharmony_ci */ 2738c2ecf20Sopenharmony_ci ia64_ptcga(start, (nbits << 2)); 2748c2ecf20Sopenharmony_ci ia64_srlz_i(); 2758c2ecf20Sopenharmony_ci start += (1UL << nbits); 2768c2ecf20Sopenharmony_ci } while (start < end); 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci if (need_ptcg_sem) 2798c2ecf20Sopenharmony_ci up_spin(&ptcg_sem); 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci if (mm != active_mm) { 2828c2ecf20Sopenharmony_ci activate_context(active_mm); 2838c2ecf20Sopenharmony_ci } 2848c2ecf20Sopenharmony_ci} 2858c2ecf20Sopenharmony_ci#endif /* CONFIG_SMP */ 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_civoid 2888c2ecf20Sopenharmony_cilocal_flush_tlb_all (void) 2898c2ecf20Sopenharmony_ci{ 2908c2ecf20Sopenharmony_ci unsigned long i, j, flags, count0, count1, stride0, stride1, addr; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci addr = local_cpu_data->ptce_base; 2938c2ecf20Sopenharmony_ci count0 = local_cpu_data->ptce_count[0]; 2948c2ecf20Sopenharmony_ci count1 = local_cpu_data->ptce_count[1]; 2958c2ecf20Sopenharmony_ci stride0 = local_cpu_data->ptce_stride[0]; 2968c2ecf20Sopenharmony_ci stride1 = local_cpu_data->ptce_stride[1]; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci local_irq_save(flags); 2998c2ecf20Sopenharmony_ci for (i = 0; i < count0; ++i) { 3008c2ecf20Sopenharmony_ci for (j = 0; j < count1; ++j) { 3018c2ecf20Sopenharmony_ci ia64_ptce(addr); 3028c2ecf20Sopenharmony_ci addr += stride1; 3038c2ecf20Sopenharmony_ci } 3048c2ecf20Sopenharmony_ci addr += stride0; 3058c2ecf20Sopenharmony_ci } 3068c2ecf20Sopenharmony_ci local_irq_restore(flags); 3078c2ecf20Sopenharmony_ci ia64_srlz_i(); /* srlz.i implies srlz.d */ 3088c2ecf20Sopenharmony_ci} 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_cistatic void 3118c2ecf20Sopenharmony_ci__flush_tlb_range (struct vm_area_struct *vma, unsigned long start, 3128c2ecf20Sopenharmony_ci unsigned long end) 3138c2ecf20Sopenharmony_ci{ 3148c2ecf20Sopenharmony_ci struct mm_struct *mm = vma->vm_mm; 3158c2ecf20Sopenharmony_ci unsigned long size = end - start; 3168c2ecf20Sopenharmony_ci unsigned long nbits; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci#ifndef CONFIG_SMP 3198c2ecf20Sopenharmony_ci if (mm != current->active_mm) { 3208c2ecf20Sopenharmony_ci mm->context = 0; 3218c2ecf20Sopenharmony_ci return; 3228c2ecf20Sopenharmony_ci } 3238c2ecf20Sopenharmony_ci#endif 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci nbits = ia64_fls(size + 0xfff); 3268c2ecf20Sopenharmony_ci while (unlikely (((1UL << nbits) & purge.mask) == 0) && 3278c2ecf20Sopenharmony_ci (nbits < purge.max_bits)) 3288c2ecf20Sopenharmony_ci ++nbits; 3298c2ecf20Sopenharmony_ci if (nbits > purge.max_bits) 3308c2ecf20Sopenharmony_ci nbits = purge.max_bits; 3318c2ecf20Sopenharmony_ci start &= ~((1UL << nbits) - 1); 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci preempt_disable(); 3348c2ecf20Sopenharmony_ci#ifdef CONFIG_SMP 3358c2ecf20Sopenharmony_ci if (mm != current->active_mm || cpumask_weight(mm_cpumask(mm)) != 1) { 3368c2ecf20Sopenharmony_ci ia64_global_tlb_purge(mm, start, end, nbits); 3378c2ecf20Sopenharmony_ci preempt_enable(); 3388c2ecf20Sopenharmony_ci return; 3398c2ecf20Sopenharmony_ci } 3408c2ecf20Sopenharmony_ci#endif 3418c2ecf20Sopenharmony_ci do { 3428c2ecf20Sopenharmony_ci ia64_ptcl(start, (nbits<<2)); 3438c2ecf20Sopenharmony_ci start += (1UL << nbits); 3448c2ecf20Sopenharmony_ci } while (start < end); 3458c2ecf20Sopenharmony_ci preempt_enable(); 3468c2ecf20Sopenharmony_ci ia64_srlz_i(); /* srlz.i implies srlz.d */ 3478c2ecf20Sopenharmony_ci} 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_civoid flush_tlb_range(struct vm_area_struct *vma, 3508c2ecf20Sopenharmony_ci unsigned long start, unsigned long end) 3518c2ecf20Sopenharmony_ci{ 3528c2ecf20Sopenharmony_ci if (unlikely(end - start >= 1024*1024*1024*1024UL 3538c2ecf20Sopenharmony_ci || REGION_NUMBER(start) != REGION_NUMBER(end - 1))) { 3548c2ecf20Sopenharmony_ci /* 3558c2ecf20Sopenharmony_ci * If we flush more than a tera-byte or across regions, we're 3568c2ecf20Sopenharmony_ci * probably better off just flushing the entire TLB(s). This 3578c2ecf20Sopenharmony_ci * should be very rare and is not worth optimizing for. 3588c2ecf20Sopenharmony_ci */ 3598c2ecf20Sopenharmony_ci flush_tlb_all(); 3608c2ecf20Sopenharmony_ci } else { 3618c2ecf20Sopenharmony_ci /* flush the address range from the tlb */ 3628c2ecf20Sopenharmony_ci __flush_tlb_range(vma, start, end); 3638c2ecf20Sopenharmony_ci /* flush the virt. page-table area mapping the addr range */ 3648c2ecf20Sopenharmony_ci __flush_tlb_range(vma, ia64_thash(start), ia64_thash(end)); 3658c2ecf20Sopenharmony_ci } 3668c2ecf20Sopenharmony_ci} 3678c2ecf20Sopenharmony_ciEXPORT_SYMBOL(flush_tlb_range); 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_civoid ia64_tlb_init(void) 3708c2ecf20Sopenharmony_ci{ 3718c2ecf20Sopenharmony_ci ia64_ptce_info_t ptce_info; 3728c2ecf20Sopenharmony_ci u64 tr_pgbits; 3738c2ecf20Sopenharmony_ci long status; 3748c2ecf20Sopenharmony_ci pal_vm_info_1_u_t vm_info_1; 3758c2ecf20Sopenharmony_ci pal_vm_info_2_u_t vm_info_2; 3768c2ecf20Sopenharmony_ci int cpu = smp_processor_id(); 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci if ((status = ia64_pal_vm_page_size(&tr_pgbits, &purge.mask)) != 0) { 3798c2ecf20Sopenharmony_ci printk(KERN_ERR "PAL_VM_PAGE_SIZE failed with status=%ld; " 3808c2ecf20Sopenharmony_ci "defaulting to architected purge page-sizes.\n", status); 3818c2ecf20Sopenharmony_ci purge.mask = 0x115557000UL; 3828c2ecf20Sopenharmony_ci } 3838c2ecf20Sopenharmony_ci purge.max_bits = ia64_fls(purge.mask); 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci ia64_get_ptce(&ptce_info); 3868c2ecf20Sopenharmony_ci local_cpu_data->ptce_base = ptce_info.base; 3878c2ecf20Sopenharmony_ci local_cpu_data->ptce_count[0] = ptce_info.count[0]; 3888c2ecf20Sopenharmony_ci local_cpu_data->ptce_count[1] = ptce_info.count[1]; 3898c2ecf20Sopenharmony_ci local_cpu_data->ptce_stride[0] = ptce_info.stride[0]; 3908c2ecf20Sopenharmony_ci local_cpu_data->ptce_stride[1] = ptce_info.stride[1]; 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci local_flush_tlb_all(); /* nuke left overs from bootstrapping... */ 3938c2ecf20Sopenharmony_ci status = ia64_pal_vm_summary(&vm_info_1, &vm_info_2); 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci if (status) { 3968c2ecf20Sopenharmony_ci printk(KERN_ERR "ia64_pal_vm_summary=%ld\n", status); 3978c2ecf20Sopenharmony_ci per_cpu(ia64_tr_num, cpu) = 8; 3988c2ecf20Sopenharmony_ci return; 3998c2ecf20Sopenharmony_ci } 4008c2ecf20Sopenharmony_ci per_cpu(ia64_tr_num, cpu) = vm_info_1.pal_vm_info_1_s.max_itr_entry+1; 4018c2ecf20Sopenharmony_ci if (per_cpu(ia64_tr_num, cpu) > 4028c2ecf20Sopenharmony_ci (vm_info_1.pal_vm_info_1_s.max_dtr_entry+1)) 4038c2ecf20Sopenharmony_ci per_cpu(ia64_tr_num, cpu) = 4048c2ecf20Sopenharmony_ci vm_info_1.pal_vm_info_1_s.max_dtr_entry+1; 4058c2ecf20Sopenharmony_ci if (per_cpu(ia64_tr_num, cpu) > IA64_TR_ALLOC_MAX) { 4068c2ecf20Sopenharmony_ci static int justonce = 1; 4078c2ecf20Sopenharmony_ci per_cpu(ia64_tr_num, cpu) = IA64_TR_ALLOC_MAX; 4088c2ecf20Sopenharmony_ci if (justonce) { 4098c2ecf20Sopenharmony_ci justonce = 0; 4108c2ecf20Sopenharmony_ci printk(KERN_DEBUG "TR register number exceeds " 4118c2ecf20Sopenharmony_ci "IA64_TR_ALLOC_MAX!\n"); 4128c2ecf20Sopenharmony_ci } 4138c2ecf20Sopenharmony_ci } 4148c2ecf20Sopenharmony_ci} 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci/* 4178c2ecf20Sopenharmony_ci * is_tr_overlap 4188c2ecf20Sopenharmony_ci * 4198c2ecf20Sopenharmony_ci * Check overlap with inserted TRs. 4208c2ecf20Sopenharmony_ci */ 4218c2ecf20Sopenharmony_cistatic int is_tr_overlap(struct ia64_tr_entry *p, u64 va, u64 log_size) 4228c2ecf20Sopenharmony_ci{ 4238c2ecf20Sopenharmony_ci u64 tr_log_size; 4248c2ecf20Sopenharmony_ci u64 tr_end; 4258c2ecf20Sopenharmony_ci u64 va_rr = ia64_get_rr(va); 4268c2ecf20Sopenharmony_ci u64 va_rid = RR_TO_RID(va_rr); 4278c2ecf20Sopenharmony_ci u64 va_end = va + (1<<log_size) - 1; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci if (va_rid != RR_TO_RID(p->rr)) 4308c2ecf20Sopenharmony_ci return 0; 4318c2ecf20Sopenharmony_ci tr_log_size = (p->itir & 0xff) >> 2; 4328c2ecf20Sopenharmony_ci tr_end = p->ifa + (1<<tr_log_size) - 1; 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci if (va > tr_end || p->ifa > va_end) 4358c2ecf20Sopenharmony_ci return 0; 4368c2ecf20Sopenharmony_ci return 1; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci} 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci/* 4418c2ecf20Sopenharmony_ci * ia64_insert_tr in virtual mode. Allocate a TR slot 4428c2ecf20Sopenharmony_ci * 4438c2ecf20Sopenharmony_ci * target_mask : 0x1 : itr, 0x2 : dtr, 0x3 : idtr 4448c2ecf20Sopenharmony_ci * 4458c2ecf20Sopenharmony_ci * va : virtual address. 4468c2ecf20Sopenharmony_ci * pte : pte entries inserted. 4478c2ecf20Sopenharmony_ci * log_size: range to be covered. 4488c2ecf20Sopenharmony_ci * 4498c2ecf20Sopenharmony_ci * Return value: <0 : error No. 4508c2ecf20Sopenharmony_ci * 4518c2ecf20Sopenharmony_ci * >=0 : slot number allocated for TR. 4528c2ecf20Sopenharmony_ci * Must be called with preemption disabled. 4538c2ecf20Sopenharmony_ci */ 4548c2ecf20Sopenharmony_ciint ia64_itr_entry(u64 target_mask, u64 va, u64 pte, u64 log_size) 4558c2ecf20Sopenharmony_ci{ 4568c2ecf20Sopenharmony_ci int i, r; 4578c2ecf20Sopenharmony_ci unsigned long psr; 4588c2ecf20Sopenharmony_ci struct ia64_tr_entry *p; 4598c2ecf20Sopenharmony_ci int cpu = smp_processor_id(); 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci if (!ia64_idtrs[cpu]) { 4628c2ecf20Sopenharmony_ci ia64_idtrs[cpu] = kmalloc_array(2 * IA64_TR_ALLOC_MAX, 4638c2ecf20Sopenharmony_ci sizeof(struct ia64_tr_entry), 4648c2ecf20Sopenharmony_ci GFP_KERNEL); 4658c2ecf20Sopenharmony_ci if (!ia64_idtrs[cpu]) 4668c2ecf20Sopenharmony_ci return -ENOMEM; 4678c2ecf20Sopenharmony_ci } 4688c2ecf20Sopenharmony_ci r = -EINVAL; 4698c2ecf20Sopenharmony_ci /*Check overlap with existing TR entries*/ 4708c2ecf20Sopenharmony_ci if (target_mask & 0x1) { 4718c2ecf20Sopenharmony_ci p = ia64_idtrs[cpu]; 4728c2ecf20Sopenharmony_ci for (i = IA64_TR_ALLOC_BASE; i <= per_cpu(ia64_tr_used, cpu); 4738c2ecf20Sopenharmony_ci i++, p++) { 4748c2ecf20Sopenharmony_ci if (p->pte & 0x1) 4758c2ecf20Sopenharmony_ci if (is_tr_overlap(p, va, log_size)) { 4768c2ecf20Sopenharmony_ci printk(KERN_DEBUG "Overlapped Entry" 4778c2ecf20Sopenharmony_ci "Inserted for TR Register!!\n"); 4788c2ecf20Sopenharmony_ci goto out; 4798c2ecf20Sopenharmony_ci } 4808c2ecf20Sopenharmony_ci } 4818c2ecf20Sopenharmony_ci } 4828c2ecf20Sopenharmony_ci if (target_mask & 0x2) { 4838c2ecf20Sopenharmony_ci p = ia64_idtrs[cpu] + IA64_TR_ALLOC_MAX; 4848c2ecf20Sopenharmony_ci for (i = IA64_TR_ALLOC_BASE; i <= per_cpu(ia64_tr_used, cpu); 4858c2ecf20Sopenharmony_ci i++, p++) { 4868c2ecf20Sopenharmony_ci if (p->pte & 0x1) 4878c2ecf20Sopenharmony_ci if (is_tr_overlap(p, va, log_size)) { 4888c2ecf20Sopenharmony_ci printk(KERN_DEBUG "Overlapped Entry" 4898c2ecf20Sopenharmony_ci "Inserted for TR Register!!\n"); 4908c2ecf20Sopenharmony_ci goto out; 4918c2ecf20Sopenharmony_ci } 4928c2ecf20Sopenharmony_ci } 4938c2ecf20Sopenharmony_ci } 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci for (i = IA64_TR_ALLOC_BASE; i < per_cpu(ia64_tr_num, cpu); i++) { 4968c2ecf20Sopenharmony_ci switch (target_mask & 0x3) { 4978c2ecf20Sopenharmony_ci case 1: 4988c2ecf20Sopenharmony_ci if (!((ia64_idtrs[cpu] + i)->pte & 0x1)) 4998c2ecf20Sopenharmony_ci goto found; 5008c2ecf20Sopenharmony_ci continue; 5018c2ecf20Sopenharmony_ci case 2: 5028c2ecf20Sopenharmony_ci if (!((ia64_idtrs[cpu] + IA64_TR_ALLOC_MAX + i)->pte & 0x1)) 5038c2ecf20Sopenharmony_ci goto found; 5048c2ecf20Sopenharmony_ci continue; 5058c2ecf20Sopenharmony_ci case 3: 5068c2ecf20Sopenharmony_ci if (!((ia64_idtrs[cpu] + i)->pte & 0x1) && 5078c2ecf20Sopenharmony_ci !((ia64_idtrs[cpu] + IA64_TR_ALLOC_MAX + i)->pte & 0x1)) 5088c2ecf20Sopenharmony_ci goto found; 5098c2ecf20Sopenharmony_ci continue; 5108c2ecf20Sopenharmony_ci default: 5118c2ecf20Sopenharmony_ci r = -EINVAL; 5128c2ecf20Sopenharmony_ci goto out; 5138c2ecf20Sopenharmony_ci } 5148c2ecf20Sopenharmony_ci } 5158c2ecf20Sopenharmony_cifound: 5168c2ecf20Sopenharmony_ci if (i >= per_cpu(ia64_tr_num, cpu)) 5178c2ecf20Sopenharmony_ci return -EBUSY; 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci /*Record tr info for mca hander use!*/ 5208c2ecf20Sopenharmony_ci if (i > per_cpu(ia64_tr_used, cpu)) 5218c2ecf20Sopenharmony_ci per_cpu(ia64_tr_used, cpu) = i; 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci psr = ia64_clear_ic(); 5248c2ecf20Sopenharmony_ci if (target_mask & 0x1) { 5258c2ecf20Sopenharmony_ci ia64_itr(0x1, i, va, pte, log_size); 5268c2ecf20Sopenharmony_ci ia64_srlz_i(); 5278c2ecf20Sopenharmony_ci p = ia64_idtrs[cpu] + i; 5288c2ecf20Sopenharmony_ci p->ifa = va; 5298c2ecf20Sopenharmony_ci p->pte = pte; 5308c2ecf20Sopenharmony_ci p->itir = log_size << 2; 5318c2ecf20Sopenharmony_ci p->rr = ia64_get_rr(va); 5328c2ecf20Sopenharmony_ci } 5338c2ecf20Sopenharmony_ci if (target_mask & 0x2) { 5348c2ecf20Sopenharmony_ci ia64_itr(0x2, i, va, pte, log_size); 5358c2ecf20Sopenharmony_ci ia64_srlz_i(); 5368c2ecf20Sopenharmony_ci p = ia64_idtrs[cpu] + IA64_TR_ALLOC_MAX + i; 5378c2ecf20Sopenharmony_ci p->ifa = va; 5388c2ecf20Sopenharmony_ci p->pte = pte; 5398c2ecf20Sopenharmony_ci p->itir = log_size << 2; 5408c2ecf20Sopenharmony_ci p->rr = ia64_get_rr(va); 5418c2ecf20Sopenharmony_ci } 5428c2ecf20Sopenharmony_ci ia64_set_psr(psr); 5438c2ecf20Sopenharmony_ci r = i; 5448c2ecf20Sopenharmony_ciout: 5458c2ecf20Sopenharmony_ci return r; 5468c2ecf20Sopenharmony_ci} 5478c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ia64_itr_entry); 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci/* 5508c2ecf20Sopenharmony_ci * ia64_purge_tr 5518c2ecf20Sopenharmony_ci * 5528c2ecf20Sopenharmony_ci * target_mask: 0x1: purge itr, 0x2 : purge dtr, 0x3 purge idtr. 5538c2ecf20Sopenharmony_ci * slot: slot number to be freed. 5548c2ecf20Sopenharmony_ci * 5558c2ecf20Sopenharmony_ci * Must be called with preemption disabled. 5568c2ecf20Sopenharmony_ci */ 5578c2ecf20Sopenharmony_civoid ia64_ptr_entry(u64 target_mask, int slot) 5588c2ecf20Sopenharmony_ci{ 5598c2ecf20Sopenharmony_ci int cpu = smp_processor_id(); 5608c2ecf20Sopenharmony_ci int i; 5618c2ecf20Sopenharmony_ci struct ia64_tr_entry *p; 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci if (slot < IA64_TR_ALLOC_BASE || slot >= per_cpu(ia64_tr_num, cpu)) 5648c2ecf20Sopenharmony_ci return; 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci if (target_mask & 0x1) { 5678c2ecf20Sopenharmony_ci p = ia64_idtrs[cpu] + slot; 5688c2ecf20Sopenharmony_ci if ((p->pte&0x1) && is_tr_overlap(p, p->ifa, p->itir>>2)) { 5698c2ecf20Sopenharmony_ci p->pte = 0; 5708c2ecf20Sopenharmony_ci ia64_ptr(0x1, p->ifa, p->itir>>2); 5718c2ecf20Sopenharmony_ci ia64_srlz_i(); 5728c2ecf20Sopenharmony_ci } 5738c2ecf20Sopenharmony_ci } 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci if (target_mask & 0x2) { 5768c2ecf20Sopenharmony_ci p = ia64_idtrs[cpu] + IA64_TR_ALLOC_MAX + slot; 5778c2ecf20Sopenharmony_ci if ((p->pte & 0x1) && is_tr_overlap(p, p->ifa, p->itir>>2)) { 5788c2ecf20Sopenharmony_ci p->pte = 0; 5798c2ecf20Sopenharmony_ci ia64_ptr(0x2, p->ifa, p->itir>>2); 5808c2ecf20Sopenharmony_ci ia64_srlz_i(); 5818c2ecf20Sopenharmony_ci } 5828c2ecf20Sopenharmony_ci } 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci for (i = per_cpu(ia64_tr_used, cpu); i >= IA64_TR_ALLOC_BASE; i--) { 5858c2ecf20Sopenharmony_ci if (((ia64_idtrs[cpu] + i)->pte & 0x1) || 5868c2ecf20Sopenharmony_ci ((ia64_idtrs[cpu] + IA64_TR_ALLOC_MAX + i)->pte & 0x1)) 5878c2ecf20Sopenharmony_ci break; 5888c2ecf20Sopenharmony_ci } 5898c2ecf20Sopenharmony_ci per_cpu(ia64_tr_used, cpu) = i; 5908c2ecf20Sopenharmony_ci} 5918c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ia64_ptr_entry); 592