162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * TLB support routines. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 1998-2001, 2003 Hewlett-Packard Co 662306a36Sopenharmony_ci * David Mosberger-Tang <davidm@hpl.hp.com> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * 08/02/00 A. Mallick <asit.k.mallick@intel.com> 962306a36Sopenharmony_ci * Modified RID allocation for SMP 1062306a36Sopenharmony_ci * Goutham Rao <goutham.rao@intel.com> 1162306a36Sopenharmony_ci * IPI based ptc implementation and A-step IPI implementation. 1262306a36Sopenharmony_ci * Rohit Seth <rohit.seth@intel.com> 1362306a36Sopenharmony_ci * Ken Chen <kenneth.w.chen@intel.com> 1462306a36Sopenharmony_ci * Christophe de Dinechin <ddd@hp.com>: Avoid ptc.e on memory allocation 1562306a36Sopenharmony_ci * Copyright (C) 2007 Intel Corp 1662306a36Sopenharmony_ci * Fenghua Yu <fenghua.yu@intel.com> 1762306a36Sopenharmony_ci * Add multiple ptc.g/ptc.ga instruction support in global tlb purge. 1862306a36Sopenharmony_ci */ 1962306a36Sopenharmony_ci#include <linux/module.h> 2062306a36Sopenharmony_ci#include <linux/init.h> 2162306a36Sopenharmony_ci#include <linux/kernel.h> 2262306a36Sopenharmony_ci#include <linux/sched.h> 2362306a36Sopenharmony_ci#include <linux/smp.h> 2462306a36Sopenharmony_ci#include <linux/mm.h> 2562306a36Sopenharmony_ci#include <linux/memblock.h> 2662306a36Sopenharmony_ci#include <linux/slab.h> 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#include <asm/delay.h> 2962306a36Sopenharmony_ci#include <asm/mmu_context.h> 3062306a36Sopenharmony_ci#include <asm/pal.h> 3162306a36Sopenharmony_ci#include <asm/tlbflush.h> 3262306a36Sopenharmony_ci#include <asm/dma.h> 3362306a36Sopenharmony_ci#include <asm/processor.h> 3462306a36Sopenharmony_ci#include <asm/sal.h> 3562306a36Sopenharmony_ci#include <asm/tlb.h> 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic struct { 3862306a36Sopenharmony_ci u64 mask; /* mask of supported purge page-sizes */ 3962306a36Sopenharmony_ci unsigned long max_bits; /* log2 of largest supported purge page-size */ 4062306a36Sopenharmony_ci} purge; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistruct ia64_ctx ia64_ctx = { 4362306a36Sopenharmony_ci .lock = __SPIN_LOCK_UNLOCKED(ia64_ctx.lock), 4462306a36Sopenharmony_ci .next = 1, 4562306a36Sopenharmony_ci .max_ctx = ~0U 4662306a36Sopenharmony_ci}; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ciDEFINE_PER_CPU(u8, ia64_need_tlb_flush); 4962306a36Sopenharmony_ciDEFINE_PER_CPU(u8, ia64_tr_num); /*Number of TR slots in current processor*/ 5062306a36Sopenharmony_ciDEFINE_PER_CPU(u8, ia64_tr_used); /*Max Slot number used by kernel*/ 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistruct ia64_tr_entry *ia64_idtrs[NR_CPUS]; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci/* 5562306a36Sopenharmony_ci * Initializes the ia64_ctx.bitmap array based on max_ctx+1. 5662306a36Sopenharmony_ci * Called after cpu_init() has setup ia64_ctx.max_ctx based on 5762306a36Sopenharmony_ci * maximum RID that is supported by boot CPU. 5862306a36Sopenharmony_ci */ 5962306a36Sopenharmony_civoid __init 6062306a36Sopenharmony_cimmu_context_init (void) 6162306a36Sopenharmony_ci{ 6262306a36Sopenharmony_ci ia64_ctx.bitmap = memblock_alloc((ia64_ctx.max_ctx + 1) >> 3, 6362306a36Sopenharmony_ci SMP_CACHE_BYTES); 6462306a36Sopenharmony_ci if (!ia64_ctx.bitmap) 6562306a36Sopenharmony_ci panic("%s: Failed to allocate %u bytes\n", __func__, 6662306a36Sopenharmony_ci (ia64_ctx.max_ctx + 1) >> 3); 6762306a36Sopenharmony_ci ia64_ctx.flushmap = memblock_alloc((ia64_ctx.max_ctx + 1) >> 3, 6862306a36Sopenharmony_ci SMP_CACHE_BYTES); 6962306a36Sopenharmony_ci if (!ia64_ctx.flushmap) 7062306a36Sopenharmony_ci panic("%s: Failed to allocate %u bytes\n", __func__, 7162306a36Sopenharmony_ci (ia64_ctx.max_ctx + 1) >> 3); 7262306a36Sopenharmony_ci} 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci/* 7562306a36Sopenharmony_ci * Acquire the ia64_ctx.lock before calling this function! 7662306a36Sopenharmony_ci */ 7762306a36Sopenharmony_civoid 7862306a36Sopenharmony_ciwrap_mmu_context (struct mm_struct *mm) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci int i, cpu; 8162306a36Sopenharmony_ci unsigned long flush_bit; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci for (i=0; i <= ia64_ctx.max_ctx / BITS_PER_LONG; i++) { 8462306a36Sopenharmony_ci flush_bit = xchg(&ia64_ctx.flushmap[i], 0); 8562306a36Sopenharmony_ci ia64_ctx.bitmap[i] ^= flush_bit; 8662306a36Sopenharmony_ci } 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci /* use offset at 300 to skip daemons */ 8962306a36Sopenharmony_ci ia64_ctx.next = find_next_zero_bit(ia64_ctx.bitmap, 9062306a36Sopenharmony_ci ia64_ctx.max_ctx, 300); 9162306a36Sopenharmony_ci ia64_ctx.limit = find_next_bit(ia64_ctx.bitmap, 9262306a36Sopenharmony_ci ia64_ctx.max_ctx, ia64_ctx.next); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci /* 9562306a36Sopenharmony_ci * can't call flush_tlb_all() here because of race condition 9662306a36Sopenharmony_ci * with O(1) scheduler [EF] 9762306a36Sopenharmony_ci */ 9862306a36Sopenharmony_ci cpu = get_cpu(); /* prevent preemption/migration */ 9962306a36Sopenharmony_ci for_each_online_cpu(i) 10062306a36Sopenharmony_ci if (i != cpu) 10162306a36Sopenharmony_ci per_cpu(ia64_need_tlb_flush, i) = 1; 10262306a36Sopenharmony_ci put_cpu(); 10362306a36Sopenharmony_ci local_flush_tlb_all(); 10462306a36Sopenharmony_ci} 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci/* 10762306a36Sopenharmony_ci * Implement "spinaphores" ... like counting semaphores, but they 10862306a36Sopenharmony_ci * spin instead of sleeping. If there are ever any other users for 10962306a36Sopenharmony_ci * this primitive it can be moved up to a spinaphore.h header. 11062306a36Sopenharmony_ci */ 11162306a36Sopenharmony_cistruct spinaphore { 11262306a36Sopenharmony_ci unsigned long ticket; 11362306a36Sopenharmony_ci unsigned long serve; 11462306a36Sopenharmony_ci}; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistatic inline void spinaphore_init(struct spinaphore *ss, int val) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci ss->ticket = 0; 11962306a36Sopenharmony_ci ss->serve = val; 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic inline void down_spin(struct spinaphore *ss) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci unsigned long t = ia64_fetchadd(1, &ss->ticket, acq), serve; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci if (time_before(t, ss->serve)) 12762306a36Sopenharmony_ci return; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci ia64_invala(); 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci for (;;) { 13262306a36Sopenharmony_ci asm volatile ("ld8.c.nc %0=[%1]" : "=r"(serve) : "r"(&ss->serve) : "memory"); 13362306a36Sopenharmony_ci if (time_before(t, serve)) 13462306a36Sopenharmony_ci return; 13562306a36Sopenharmony_ci cpu_relax(); 13662306a36Sopenharmony_ci } 13762306a36Sopenharmony_ci} 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_cistatic inline void up_spin(struct spinaphore *ss) 14062306a36Sopenharmony_ci{ 14162306a36Sopenharmony_ci ia64_fetchadd(1, &ss->serve, rel); 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic struct spinaphore ptcg_sem; 14562306a36Sopenharmony_cistatic u16 nptcg = 1; 14662306a36Sopenharmony_cistatic int need_ptcg_sem = 1; 14762306a36Sopenharmony_cistatic int toolatetochangeptcgsem = 0; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci/* 15062306a36Sopenharmony_ci * Kernel parameter "nptcg=" overrides max number of concurrent global TLB 15162306a36Sopenharmony_ci * purges which is reported from either PAL or SAL PALO. 15262306a36Sopenharmony_ci * 15362306a36Sopenharmony_ci * We don't have sanity checking for nptcg value. It's the user's responsibility 15462306a36Sopenharmony_ci * for valid nptcg value on the platform. Otherwise, kernel may hang in some 15562306a36Sopenharmony_ci * cases. 15662306a36Sopenharmony_ci */ 15762306a36Sopenharmony_cistatic int __init 15862306a36Sopenharmony_ciset_nptcg(char *str) 15962306a36Sopenharmony_ci{ 16062306a36Sopenharmony_ci int value = 0; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci get_option(&str, &value); 16362306a36Sopenharmony_ci setup_ptcg_sem(value, NPTCG_FROM_KERNEL_PARAMETER); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci return 1; 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci__setup("nptcg=", set_nptcg); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci/* 17162306a36Sopenharmony_ci * Maximum number of simultaneous ptc.g purges in the system can 17262306a36Sopenharmony_ci * be defined by PAL_VM_SUMMARY (in which case we should take 17362306a36Sopenharmony_ci * the smallest value for any cpu in the system) or by the PAL 17462306a36Sopenharmony_ci * override table (in which case we should ignore the value from 17562306a36Sopenharmony_ci * PAL_VM_SUMMARY). 17662306a36Sopenharmony_ci * 17762306a36Sopenharmony_ci * Kernel parameter "nptcg=" overrides maximum number of simultaneous ptc.g 17862306a36Sopenharmony_ci * purges defined in either PAL_VM_SUMMARY or PAL override table. In this case, 17962306a36Sopenharmony_ci * we should ignore the value from either PAL_VM_SUMMARY or PAL override table. 18062306a36Sopenharmony_ci * 18162306a36Sopenharmony_ci * Complicating the logic here is the fact that num_possible_cpus() 18262306a36Sopenharmony_ci * isn't fully setup until we start bringing cpus online. 18362306a36Sopenharmony_ci */ 18462306a36Sopenharmony_civoid 18562306a36Sopenharmony_cisetup_ptcg_sem(int max_purges, int nptcg_from) 18662306a36Sopenharmony_ci{ 18762306a36Sopenharmony_ci static int kp_override; 18862306a36Sopenharmony_ci static int palo_override; 18962306a36Sopenharmony_ci static int firstcpu = 1; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci if (toolatetochangeptcgsem) { 19262306a36Sopenharmony_ci if (nptcg_from == NPTCG_FROM_PAL && max_purges == 0) 19362306a36Sopenharmony_ci BUG_ON(1 < nptcg); 19462306a36Sopenharmony_ci else 19562306a36Sopenharmony_ci BUG_ON(max_purges < nptcg); 19662306a36Sopenharmony_ci return; 19762306a36Sopenharmony_ci } 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci if (nptcg_from == NPTCG_FROM_KERNEL_PARAMETER) { 20062306a36Sopenharmony_ci kp_override = 1; 20162306a36Sopenharmony_ci nptcg = max_purges; 20262306a36Sopenharmony_ci goto resetsema; 20362306a36Sopenharmony_ci } 20462306a36Sopenharmony_ci if (kp_override) { 20562306a36Sopenharmony_ci need_ptcg_sem = num_possible_cpus() > nptcg; 20662306a36Sopenharmony_ci return; 20762306a36Sopenharmony_ci } 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci if (nptcg_from == NPTCG_FROM_PALO) { 21062306a36Sopenharmony_ci palo_override = 1; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci /* In PALO max_purges == 0 really means it! */ 21362306a36Sopenharmony_ci if (max_purges == 0) 21462306a36Sopenharmony_ci panic("Whoa! Platform does not support global TLB purges.\n"); 21562306a36Sopenharmony_ci nptcg = max_purges; 21662306a36Sopenharmony_ci if (nptcg == PALO_MAX_TLB_PURGES) { 21762306a36Sopenharmony_ci need_ptcg_sem = 0; 21862306a36Sopenharmony_ci return; 21962306a36Sopenharmony_ci } 22062306a36Sopenharmony_ci goto resetsema; 22162306a36Sopenharmony_ci } 22262306a36Sopenharmony_ci if (palo_override) { 22362306a36Sopenharmony_ci if (nptcg != PALO_MAX_TLB_PURGES) 22462306a36Sopenharmony_ci need_ptcg_sem = (num_possible_cpus() > nptcg); 22562306a36Sopenharmony_ci return; 22662306a36Sopenharmony_ci } 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci /* In PAL_VM_SUMMARY max_purges == 0 actually means 1 */ 22962306a36Sopenharmony_ci if (max_purges == 0) max_purges = 1; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci if (firstcpu) { 23262306a36Sopenharmony_ci nptcg = max_purges; 23362306a36Sopenharmony_ci firstcpu = 0; 23462306a36Sopenharmony_ci } 23562306a36Sopenharmony_ci if (max_purges < nptcg) 23662306a36Sopenharmony_ci nptcg = max_purges; 23762306a36Sopenharmony_ci if (nptcg == PAL_MAX_PURGES) { 23862306a36Sopenharmony_ci need_ptcg_sem = 0; 23962306a36Sopenharmony_ci return; 24062306a36Sopenharmony_ci } else 24162306a36Sopenharmony_ci need_ptcg_sem = (num_possible_cpus() > nptcg); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ciresetsema: 24462306a36Sopenharmony_ci spinaphore_init(&ptcg_sem, max_purges); 24562306a36Sopenharmony_ci} 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci#ifdef CONFIG_SMP 24862306a36Sopenharmony_cistatic void 24962306a36Sopenharmony_ciia64_global_tlb_purge (struct mm_struct *mm, unsigned long start, 25062306a36Sopenharmony_ci unsigned long end, unsigned long nbits) 25162306a36Sopenharmony_ci{ 25262306a36Sopenharmony_ci struct mm_struct *active_mm = current->active_mm; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci toolatetochangeptcgsem = 1; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci if (mm != active_mm) { 25762306a36Sopenharmony_ci /* Restore region IDs for mm */ 25862306a36Sopenharmony_ci if (mm && active_mm) { 25962306a36Sopenharmony_ci activate_context(mm); 26062306a36Sopenharmony_ci } else { 26162306a36Sopenharmony_ci flush_tlb_all(); 26262306a36Sopenharmony_ci return; 26362306a36Sopenharmony_ci } 26462306a36Sopenharmony_ci } 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci if (need_ptcg_sem) 26762306a36Sopenharmony_ci down_spin(&ptcg_sem); 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci do { 27062306a36Sopenharmony_ci /* 27162306a36Sopenharmony_ci * Flush ALAT entries also. 27262306a36Sopenharmony_ci */ 27362306a36Sopenharmony_ci ia64_ptcga(start, (nbits << 2)); 27462306a36Sopenharmony_ci ia64_srlz_i(); 27562306a36Sopenharmony_ci start += (1UL << nbits); 27662306a36Sopenharmony_ci } while (start < end); 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci if (need_ptcg_sem) 27962306a36Sopenharmony_ci up_spin(&ptcg_sem); 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci if (mm != active_mm) { 28262306a36Sopenharmony_ci activate_context(active_mm); 28362306a36Sopenharmony_ci } 28462306a36Sopenharmony_ci} 28562306a36Sopenharmony_ci#endif /* CONFIG_SMP */ 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_civoid 28862306a36Sopenharmony_cilocal_flush_tlb_all (void) 28962306a36Sopenharmony_ci{ 29062306a36Sopenharmony_ci unsigned long i, j, flags, count0, count1, stride0, stride1, addr; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci addr = local_cpu_data->ptce_base; 29362306a36Sopenharmony_ci count0 = local_cpu_data->ptce_count[0]; 29462306a36Sopenharmony_ci count1 = local_cpu_data->ptce_count[1]; 29562306a36Sopenharmony_ci stride0 = local_cpu_data->ptce_stride[0]; 29662306a36Sopenharmony_ci stride1 = local_cpu_data->ptce_stride[1]; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci local_irq_save(flags); 29962306a36Sopenharmony_ci for (i = 0; i < count0; ++i) { 30062306a36Sopenharmony_ci for (j = 0; j < count1; ++j) { 30162306a36Sopenharmony_ci ia64_ptce(addr); 30262306a36Sopenharmony_ci addr += stride1; 30362306a36Sopenharmony_ci } 30462306a36Sopenharmony_ci addr += stride0; 30562306a36Sopenharmony_ci } 30662306a36Sopenharmony_ci local_irq_restore(flags); 30762306a36Sopenharmony_ci ia64_srlz_i(); /* srlz.i implies srlz.d */ 30862306a36Sopenharmony_ci} 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_cistatic void 31162306a36Sopenharmony_ci__flush_tlb_range (struct vm_area_struct *vma, unsigned long start, 31262306a36Sopenharmony_ci unsigned long end) 31362306a36Sopenharmony_ci{ 31462306a36Sopenharmony_ci struct mm_struct *mm = vma->vm_mm; 31562306a36Sopenharmony_ci unsigned long size = end - start; 31662306a36Sopenharmony_ci unsigned long nbits; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci#ifndef CONFIG_SMP 31962306a36Sopenharmony_ci if (mm != current->active_mm) { 32062306a36Sopenharmony_ci mm->context = 0; 32162306a36Sopenharmony_ci return; 32262306a36Sopenharmony_ci } 32362306a36Sopenharmony_ci#endif 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci nbits = ia64_fls(size + 0xfff); 32662306a36Sopenharmony_ci while (unlikely (((1UL << nbits) & purge.mask) == 0) && 32762306a36Sopenharmony_ci (nbits < purge.max_bits)) 32862306a36Sopenharmony_ci ++nbits; 32962306a36Sopenharmony_ci if (nbits > purge.max_bits) 33062306a36Sopenharmony_ci nbits = purge.max_bits; 33162306a36Sopenharmony_ci start &= ~((1UL << nbits) - 1); 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci preempt_disable(); 33462306a36Sopenharmony_ci#ifdef CONFIG_SMP 33562306a36Sopenharmony_ci if (mm != current->active_mm || cpumask_weight(mm_cpumask(mm)) != 1) { 33662306a36Sopenharmony_ci ia64_global_tlb_purge(mm, start, end, nbits); 33762306a36Sopenharmony_ci preempt_enable(); 33862306a36Sopenharmony_ci return; 33962306a36Sopenharmony_ci } 34062306a36Sopenharmony_ci#endif 34162306a36Sopenharmony_ci do { 34262306a36Sopenharmony_ci ia64_ptcl(start, (nbits<<2)); 34362306a36Sopenharmony_ci start += (1UL << nbits); 34462306a36Sopenharmony_ci } while (start < end); 34562306a36Sopenharmony_ci preempt_enable(); 34662306a36Sopenharmony_ci ia64_srlz_i(); /* srlz.i implies srlz.d */ 34762306a36Sopenharmony_ci} 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_civoid flush_tlb_range(struct vm_area_struct *vma, 35062306a36Sopenharmony_ci unsigned long start, unsigned long end) 35162306a36Sopenharmony_ci{ 35262306a36Sopenharmony_ci if (unlikely(end - start >= 1024*1024*1024*1024UL 35362306a36Sopenharmony_ci || REGION_NUMBER(start) != REGION_NUMBER(end - 1))) { 35462306a36Sopenharmony_ci /* 35562306a36Sopenharmony_ci * If we flush more than a tera-byte or across regions, we're 35662306a36Sopenharmony_ci * probably better off just flushing the entire TLB(s). This 35762306a36Sopenharmony_ci * should be very rare and is not worth optimizing for. 35862306a36Sopenharmony_ci */ 35962306a36Sopenharmony_ci flush_tlb_all(); 36062306a36Sopenharmony_ci } else { 36162306a36Sopenharmony_ci /* flush the address range from the tlb */ 36262306a36Sopenharmony_ci __flush_tlb_range(vma, start, end); 36362306a36Sopenharmony_ci /* flush the virt. page-table area mapping the addr range */ 36462306a36Sopenharmony_ci __flush_tlb_range(vma, ia64_thash(start), ia64_thash(end)); 36562306a36Sopenharmony_ci } 36662306a36Sopenharmony_ci} 36762306a36Sopenharmony_ciEXPORT_SYMBOL(flush_tlb_range); 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_civoid ia64_tlb_init(void) 37062306a36Sopenharmony_ci{ 37162306a36Sopenharmony_ci ia64_ptce_info_t ptce_info; 37262306a36Sopenharmony_ci u64 tr_pgbits; 37362306a36Sopenharmony_ci long status; 37462306a36Sopenharmony_ci pal_vm_info_1_u_t vm_info_1; 37562306a36Sopenharmony_ci pal_vm_info_2_u_t vm_info_2; 37662306a36Sopenharmony_ci int cpu = smp_processor_id(); 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci if ((status = ia64_pal_vm_page_size(&tr_pgbits, &purge.mask)) != 0) { 37962306a36Sopenharmony_ci printk(KERN_ERR "PAL_VM_PAGE_SIZE failed with status=%ld; " 38062306a36Sopenharmony_ci "defaulting to architected purge page-sizes.\n", status); 38162306a36Sopenharmony_ci purge.mask = 0x115557000UL; 38262306a36Sopenharmony_ci } 38362306a36Sopenharmony_ci purge.max_bits = ia64_fls(purge.mask); 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci ia64_get_ptce(&ptce_info); 38662306a36Sopenharmony_ci local_cpu_data->ptce_base = ptce_info.base; 38762306a36Sopenharmony_ci local_cpu_data->ptce_count[0] = ptce_info.count[0]; 38862306a36Sopenharmony_ci local_cpu_data->ptce_count[1] = ptce_info.count[1]; 38962306a36Sopenharmony_ci local_cpu_data->ptce_stride[0] = ptce_info.stride[0]; 39062306a36Sopenharmony_ci local_cpu_data->ptce_stride[1] = ptce_info.stride[1]; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci local_flush_tlb_all(); /* nuke left overs from bootstrapping... */ 39362306a36Sopenharmony_ci status = ia64_pal_vm_summary(&vm_info_1, &vm_info_2); 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci if (status) { 39662306a36Sopenharmony_ci printk(KERN_ERR "ia64_pal_vm_summary=%ld\n", status); 39762306a36Sopenharmony_ci per_cpu(ia64_tr_num, cpu) = 8; 39862306a36Sopenharmony_ci return; 39962306a36Sopenharmony_ci } 40062306a36Sopenharmony_ci per_cpu(ia64_tr_num, cpu) = vm_info_1.pal_vm_info_1_s.max_itr_entry+1; 40162306a36Sopenharmony_ci if (per_cpu(ia64_tr_num, cpu) > 40262306a36Sopenharmony_ci (vm_info_1.pal_vm_info_1_s.max_dtr_entry+1)) 40362306a36Sopenharmony_ci per_cpu(ia64_tr_num, cpu) = 40462306a36Sopenharmony_ci vm_info_1.pal_vm_info_1_s.max_dtr_entry+1; 40562306a36Sopenharmony_ci if (per_cpu(ia64_tr_num, cpu) > IA64_TR_ALLOC_MAX) { 40662306a36Sopenharmony_ci static int justonce = 1; 40762306a36Sopenharmony_ci per_cpu(ia64_tr_num, cpu) = IA64_TR_ALLOC_MAX; 40862306a36Sopenharmony_ci if (justonce) { 40962306a36Sopenharmony_ci justonce = 0; 41062306a36Sopenharmony_ci printk(KERN_DEBUG "TR register number exceeds " 41162306a36Sopenharmony_ci "IA64_TR_ALLOC_MAX!\n"); 41262306a36Sopenharmony_ci } 41362306a36Sopenharmony_ci } 41462306a36Sopenharmony_ci} 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci/* 41762306a36Sopenharmony_ci * is_tr_overlap 41862306a36Sopenharmony_ci * 41962306a36Sopenharmony_ci * Check overlap with inserted TRs. 42062306a36Sopenharmony_ci */ 42162306a36Sopenharmony_cistatic int is_tr_overlap(struct ia64_tr_entry *p, u64 va, u64 log_size) 42262306a36Sopenharmony_ci{ 42362306a36Sopenharmony_ci u64 tr_log_size; 42462306a36Sopenharmony_ci u64 tr_end; 42562306a36Sopenharmony_ci u64 va_rr = ia64_get_rr(va); 42662306a36Sopenharmony_ci u64 va_rid = RR_TO_RID(va_rr); 42762306a36Sopenharmony_ci u64 va_end = va + (1<<log_size) - 1; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci if (va_rid != RR_TO_RID(p->rr)) 43062306a36Sopenharmony_ci return 0; 43162306a36Sopenharmony_ci tr_log_size = (p->itir & 0xff) >> 2; 43262306a36Sopenharmony_ci tr_end = p->ifa + (1<<tr_log_size) - 1; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci if (va > tr_end || p->ifa > va_end) 43562306a36Sopenharmony_ci return 0; 43662306a36Sopenharmony_ci return 1; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci} 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci/* 44162306a36Sopenharmony_ci * ia64_insert_tr in virtual mode. Allocate a TR slot 44262306a36Sopenharmony_ci * 44362306a36Sopenharmony_ci * target_mask : 0x1 : itr, 0x2 : dtr, 0x3 : idtr 44462306a36Sopenharmony_ci * 44562306a36Sopenharmony_ci * va : virtual address. 44662306a36Sopenharmony_ci * pte : pte entries inserted. 44762306a36Sopenharmony_ci * log_size: range to be covered. 44862306a36Sopenharmony_ci * 44962306a36Sopenharmony_ci * Return value: <0 : error No. 45062306a36Sopenharmony_ci * 45162306a36Sopenharmony_ci * >=0 : slot number allocated for TR. 45262306a36Sopenharmony_ci * Must be called with preemption disabled. 45362306a36Sopenharmony_ci */ 45462306a36Sopenharmony_ciint ia64_itr_entry(u64 target_mask, u64 va, u64 pte, u64 log_size) 45562306a36Sopenharmony_ci{ 45662306a36Sopenharmony_ci int i, r; 45762306a36Sopenharmony_ci unsigned long psr; 45862306a36Sopenharmony_ci struct ia64_tr_entry *p; 45962306a36Sopenharmony_ci int cpu = smp_processor_id(); 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci if (!ia64_idtrs[cpu]) { 46262306a36Sopenharmony_ci ia64_idtrs[cpu] = kmalloc_array(2 * IA64_TR_ALLOC_MAX, 46362306a36Sopenharmony_ci sizeof(struct ia64_tr_entry), 46462306a36Sopenharmony_ci GFP_KERNEL); 46562306a36Sopenharmony_ci if (!ia64_idtrs[cpu]) 46662306a36Sopenharmony_ci return -ENOMEM; 46762306a36Sopenharmony_ci } 46862306a36Sopenharmony_ci r = -EINVAL; 46962306a36Sopenharmony_ci /*Check overlap with existing TR entries*/ 47062306a36Sopenharmony_ci if (target_mask & 0x1) { 47162306a36Sopenharmony_ci p = ia64_idtrs[cpu]; 47262306a36Sopenharmony_ci for (i = IA64_TR_ALLOC_BASE; i <= per_cpu(ia64_tr_used, cpu); 47362306a36Sopenharmony_ci i++, p++) { 47462306a36Sopenharmony_ci if (p->pte & 0x1) 47562306a36Sopenharmony_ci if (is_tr_overlap(p, va, log_size)) { 47662306a36Sopenharmony_ci printk(KERN_DEBUG "Overlapped Entry" 47762306a36Sopenharmony_ci "Inserted for TR Register!!\n"); 47862306a36Sopenharmony_ci goto out; 47962306a36Sopenharmony_ci } 48062306a36Sopenharmony_ci } 48162306a36Sopenharmony_ci } 48262306a36Sopenharmony_ci if (target_mask & 0x2) { 48362306a36Sopenharmony_ci p = ia64_idtrs[cpu] + IA64_TR_ALLOC_MAX; 48462306a36Sopenharmony_ci for (i = IA64_TR_ALLOC_BASE; i <= per_cpu(ia64_tr_used, cpu); 48562306a36Sopenharmony_ci i++, p++) { 48662306a36Sopenharmony_ci if (p->pte & 0x1) 48762306a36Sopenharmony_ci if (is_tr_overlap(p, va, log_size)) { 48862306a36Sopenharmony_ci printk(KERN_DEBUG "Overlapped Entry" 48962306a36Sopenharmony_ci "Inserted for TR Register!!\n"); 49062306a36Sopenharmony_ci goto out; 49162306a36Sopenharmony_ci } 49262306a36Sopenharmony_ci } 49362306a36Sopenharmony_ci } 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci for (i = IA64_TR_ALLOC_BASE; i < per_cpu(ia64_tr_num, cpu); i++) { 49662306a36Sopenharmony_ci switch (target_mask & 0x3) { 49762306a36Sopenharmony_ci case 1: 49862306a36Sopenharmony_ci if (!((ia64_idtrs[cpu] + i)->pte & 0x1)) 49962306a36Sopenharmony_ci goto found; 50062306a36Sopenharmony_ci continue; 50162306a36Sopenharmony_ci case 2: 50262306a36Sopenharmony_ci if (!((ia64_idtrs[cpu] + IA64_TR_ALLOC_MAX + i)->pte & 0x1)) 50362306a36Sopenharmony_ci goto found; 50462306a36Sopenharmony_ci continue; 50562306a36Sopenharmony_ci case 3: 50662306a36Sopenharmony_ci if (!((ia64_idtrs[cpu] + i)->pte & 0x1) && 50762306a36Sopenharmony_ci !((ia64_idtrs[cpu] + IA64_TR_ALLOC_MAX + i)->pte & 0x1)) 50862306a36Sopenharmony_ci goto found; 50962306a36Sopenharmony_ci continue; 51062306a36Sopenharmony_ci default: 51162306a36Sopenharmony_ci r = -EINVAL; 51262306a36Sopenharmony_ci goto out; 51362306a36Sopenharmony_ci } 51462306a36Sopenharmony_ci } 51562306a36Sopenharmony_cifound: 51662306a36Sopenharmony_ci if (i >= per_cpu(ia64_tr_num, cpu)) 51762306a36Sopenharmony_ci return -EBUSY; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci /*Record tr info for mca handler use!*/ 52062306a36Sopenharmony_ci if (i > per_cpu(ia64_tr_used, cpu)) 52162306a36Sopenharmony_ci per_cpu(ia64_tr_used, cpu) = i; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci psr = ia64_clear_ic(); 52462306a36Sopenharmony_ci if (target_mask & 0x1) { 52562306a36Sopenharmony_ci ia64_itr(0x1, i, va, pte, log_size); 52662306a36Sopenharmony_ci ia64_srlz_i(); 52762306a36Sopenharmony_ci p = ia64_idtrs[cpu] + i; 52862306a36Sopenharmony_ci p->ifa = va; 52962306a36Sopenharmony_ci p->pte = pte; 53062306a36Sopenharmony_ci p->itir = log_size << 2; 53162306a36Sopenharmony_ci p->rr = ia64_get_rr(va); 53262306a36Sopenharmony_ci } 53362306a36Sopenharmony_ci if (target_mask & 0x2) { 53462306a36Sopenharmony_ci ia64_itr(0x2, i, va, pte, log_size); 53562306a36Sopenharmony_ci ia64_srlz_i(); 53662306a36Sopenharmony_ci p = ia64_idtrs[cpu] + IA64_TR_ALLOC_MAX + i; 53762306a36Sopenharmony_ci p->ifa = va; 53862306a36Sopenharmony_ci p->pte = pte; 53962306a36Sopenharmony_ci p->itir = log_size << 2; 54062306a36Sopenharmony_ci p->rr = ia64_get_rr(va); 54162306a36Sopenharmony_ci } 54262306a36Sopenharmony_ci ia64_set_psr(psr); 54362306a36Sopenharmony_ci r = i; 54462306a36Sopenharmony_ciout: 54562306a36Sopenharmony_ci return r; 54662306a36Sopenharmony_ci} 54762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ia64_itr_entry); 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci/* 55062306a36Sopenharmony_ci * ia64_purge_tr 55162306a36Sopenharmony_ci * 55262306a36Sopenharmony_ci * target_mask: 0x1: purge itr, 0x2 : purge dtr, 0x3 purge idtr. 55362306a36Sopenharmony_ci * slot: slot number to be freed. 55462306a36Sopenharmony_ci * 55562306a36Sopenharmony_ci * Must be called with preemption disabled. 55662306a36Sopenharmony_ci */ 55762306a36Sopenharmony_civoid ia64_ptr_entry(u64 target_mask, int slot) 55862306a36Sopenharmony_ci{ 55962306a36Sopenharmony_ci int cpu = smp_processor_id(); 56062306a36Sopenharmony_ci int i; 56162306a36Sopenharmony_ci struct ia64_tr_entry *p; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci if (slot < IA64_TR_ALLOC_BASE || slot >= per_cpu(ia64_tr_num, cpu)) 56462306a36Sopenharmony_ci return; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci if (target_mask & 0x1) { 56762306a36Sopenharmony_ci p = ia64_idtrs[cpu] + slot; 56862306a36Sopenharmony_ci if ((p->pte&0x1) && is_tr_overlap(p, p->ifa, p->itir>>2)) { 56962306a36Sopenharmony_ci p->pte = 0; 57062306a36Sopenharmony_ci ia64_ptr(0x1, p->ifa, p->itir>>2); 57162306a36Sopenharmony_ci ia64_srlz_i(); 57262306a36Sopenharmony_ci } 57362306a36Sopenharmony_ci } 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci if (target_mask & 0x2) { 57662306a36Sopenharmony_ci p = ia64_idtrs[cpu] + IA64_TR_ALLOC_MAX + slot; 57762306a36Sopenharmony_ci if ((p->pte & 0x1) && is_tr_overlap(p, p->ifa, p->itir>>2)) { 57862306a36Sopenharmony_ci p->pte = 0; 57962306a36Sopenharmony_ci ia64_ptr(0x2, p->ifa, p->itir>>2); 58062306a36Sopenharmony_ci ia64_srlz_i(); 58162306a36Sopenharmony_ci } 58262306a36Sopenharmony_ci } 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci for (i = per_cpu(ia64_tr_used, cpu); i >= IA64_TR_ALLOC_BASE; i--) { 58562306a36Sopenharmony_ci if (((ia64_idtrs[cpu] + i)->pte & 0x1) || 58662306a36Sopenharmony_ci ((ia64_idtrs[cpu] + IA64_TR_ALLOC_MAX + i)->pte & 0x1)) 58762306a36Sopenharmony_ci break; 58862306a36Sopenharmony_ci } 58962306a36Sopenharmony_ci per_cpu(ia64_tr_used, cpu) = i; 59062306a36Sopenharmony_ci} 59162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ia64_ptr_entry); 592