162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2012 Regents of the University of California 462306a36Sopenharmony_ci * Copyright (C) 2017 SiFive 562306a36Sopenharmony_ci * Copyright (C) 2021 Western Digital Corporation or its affiliates. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/bitops.h> 962306a36Sopenharmony_ci#include <linux/cpumask.h> 1062306a36Sopenharmony_ci#include <linux/mm.h> 1162306a36Sopenharmony_ci#include <linux/percpu.h> 1262306a36Sopenharmony_ci#include <linux/slab.h> 1362306a36Sopenharmony_ci#include <linux/spinlock.h> 1462306a36Sopenharmony_ci#include <linux/static_key.h> 1562306a36Sopenharmony_ci#include <asm/tlbflush.h> 1662306a36Sopenharmony_ci#include <asm/cacheflush.h> 1762306a36Sopenharmony_ci#include <asm/mmu_context.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#ifdef CONFIG_MMU 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ciDEFINE_STATIC_KEY_FALSE(use_asid_allocator); 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistatic unsigned long asid_bits; 2462306a36Sopenharmony_cistatic unsigned long num_asids; 2562306a36Sopenharmony_ciunsigned long asid_mask; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistatic atomic_long_t current_version; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistatic DEFINE_RAW_SPINLOCK(context_lock); 3062306a36Sopenharmony_cistatic cpumask_t context_tlb_flush_pending; 3162306a36Sopenharmony_cistatic unsigned long *context_asid_map; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic DEFINE_PER_CPU(atomic_long_t, active_context); 3462306a36Sopenharmony_cistatic DEFINE_PER_CPU(unsigned long, reserved_context); 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistatic bool check_update_reserved_context(unsigned long cntx, 3762306a36Sopenharmony_ci unsigned long newcntx) 3862306a36Sopenharmony_ci{ 3962306a36Sopenharmony_ci int cpu; 4062306a36Sopenharmony_ci bool hit = false; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci /* 4362306a36Sopenharmony_ci * Iterate over the set of reserved CONTEXT looking for a match. 4462306a36Sopenharmony_ci * If we find one, then we can update our mm to use new CONTEXT 4562306a36Sopenharmony_ci * (i.e. the same CONTEXT in the current_version) but we can't 4662306a36Sopenharmony_ci * exit the loop early, since we need to ensure that all copies 4762306a36Sopenharmony_ci * of the old CONTEXT are updated to reflect the mm. Failure to do 4862306a36Sopenharmony_ci * so could result in us missing the reserved CONTEXT in a future 4962306a36Sopenharmony_ci * version. 5062306a36Sopenharmony_ci */ 5162306a36Sopenharmony_ci for_each_possible_cpu(cpu) { 5262306a36Sopenharmony_ci if (per_cpu(reserved_context, cpu) == cntx) { 5362306a36Sopenharmony_ci hit = true; 5462306a36Sopenharmony_ci per_cpu(reserved_context, cpu) = newcntx; 5562306a36Sopenharmony_ci } 5662306a36Sopenharmony_ci } 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci return hit; 5962306a36Sopenharmony_ci} 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistatic void __flush_context(void) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci int i; 6462306a36Sopenharmony_ci unsigned long cntx; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci /* Must be called with context_lock held */ 6762306a36Sopenharmony_ci lockdep_assert_held(&context_lock); 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci /* Update the list of reserved ASIDs and the ASID bitmap. */ 7062306a36Sopenharmony_ci bitmap_zero(context_asid_map, num_asids); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci /* Mark already active ASIDs as used */ 7362306a36Sopenharmony_ci for_each_possible_cpu(i) { 7462306a36Sopenharmony_ci cntx = atomic_long_xchg_relaxed(&per_cpu(active_context, i), 0); 7562306a36Sopenharmony_ci /* 7662306a36Sopenharmony_ci * If this CPU has already been through a rollover, but 7762306a36Sopenharmony_ci * hasn't run another task in the meantime, we must preserve 7862306a36Sopenharmony_ci * its reserved CONTEXT, as this is the only trace we have of 7962306a36Sopenharmony_ci * the process it is still running. 8062306a36Sopenharmony_ci */ 8162306a36Sopenharmony_ci if (cntx == 0) 8262306a36Sopenharmony_ci cntx = per_cpu(reserved_context, i); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci __set_bit(cntx & asid_mask, context_asid_map); 8562306a36Sopenharmony_ci per_cpu(reserved_context, i) = cntx; 8662306a36Sopenharmony_ci } 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci /* Mark ASID #0 as used because it is used at boot-time */ 8962306a36Sopenharmony_ci __set_bit(0, context_asid_map); 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci /* Queue a TLB invalidation for each CPU on next context-switch */ 9262306a36Sopenharmony_ci cpumask_setall(&context_tlb_flush_pending); 9362306a36Sopenharmony_ci} 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_cistatic unsigned long __new_context(struct mm_struct *mm) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci static u32 cur_idx = 1; 9862306a36Sopenharmony_ci unsigned long cntx = atomic_long_read(&mm->context.id); 9962306a36Sopenharmony_ci unsigned long asid, ver = atomic_long_read(¤t_version); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci /* Must be called with context_lock held */ 10262306a36Sopenharmony_ci lockdep_assert_held(&context_lock); 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci if (cntx != 0) { 10562306a36Sopenharmony_ci unsigned long newcntx = ver | (cntx & asid_mask); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci /* 10862306a36Sopenharmony_ci * If our current CONTEXT was active during a rollover, we 10962306a36Sopenharmony_ci * can continue to use it and this was just a false alarm. 11062306a36Sopenharmony_ci */ 11162306a36Sopenharmony_ci if (check_update_reserved_context(cntx, newcntx)) 11262306a36Sopenharmony_ci return newcntx; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci /* 11562306a36Sopenharmony_ci * We had a valid CONTEXT in a previous life, so try to 11662306a36Sopenharmony_ci * re-use it if possible. 11762306a36Sopenharmony_ci */ 11862306a36Sopenharmony_ci if (!__test_and_set_bit(cntx & asid_mask, context_asid_map)) 11962306a36Sopenharmony_ci return newcntx; 12062306a36Sopenharmony_ci } 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci /* 12362306a36Sopenharmony_ci * Allocate a free ASID. If we can't find one then increment 12462306a36Sopenharmony_ci * current_version and flush all ASIDs. 12562306a36Sopenharmony_ci */ 12662306a36Sopenharmony_ci asid = find_next_zero_bit(context_asid_map, num_asids, cur_idx); 12762306a36Sopenharmony_ci if (asid != num_asids) 12862306a36Sopenharmony_ci goto set_asid; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci /* We're out of ASIDs, so increment current_version */ 13162306a36Sopenharmony_ci ver = atomic_long_add_return_relaxed(num_asids, ¤t_version); 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci /* Flush everything */ 13462306a36Sopenharmony_ci __flush_context(); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci /* We have more ASIDs than CPUs, so this will always succeed */ 13762306a36Sopenharmony_ci asid = find_next_zero_bit(context_asid_map, num_asids, 1); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ciset_asid: 14062306a36Sopenharmony_ci __set_bit(asid, context_asid_map); 14162306a36Sopenharmony_ci cur_idx = asid; 14262306a36Sopenharmony_ci return asid | ver; 14362306a36Sopenharmony_ci} 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_cistatic void set_mm_asid(struct mm_struct *mm, unsigned int cpu) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci unsigned long flags; 14862306a36Sopenharmony_ci bool need_flush_tlb = false; 14962306a36Sopenharmony_ci unsigned long cntx, old_active_cntx; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci cntx = atomic_long_read(&mm->context.id); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci /* 15462306a36Sopenharmony_ci * If our active_context is non-zero and the context matches the 15562306a36Sopenharmony_ci * current_version, then we update the active_context entry with a 15662306a36Sopenharmony_ci * relaxed cmpxchg. 15762306a36Sopenharmony_ci * 15862306a36Sopenharmony_ci * Following is how we handle racing with a concurrent rollover: 15962306a36Sopenharmony_ci * 16062306a36Sopenharmony_ci * - We get a zero back from the cmpxchg and end up waiting on the 16162306a36Sopenharmony_ci * lock. Taking the lock synchronises with the rollover and so 16262306a36Sopenharmony_ci * we are forced to see the updated verion. 16362306a36Sopenharmony_ci * 16462306a36Sopenharmony_ci * - We get a valid context back from the cmpxchg then we continue 16562306a36Sopenharmony_ci * using old ASID because __flush_context() would have marked ASID 16662306a36Sopenharmony_ci * of active_context as used and next context switch we will 16762306a36Sopenharmony_ci * allocate new context. 16862306a36Sopenharmony_ci */ 16962306a36Sopenharmony_ci old_active_cntx = atomic_long_read(&per_cpu(active_context, cpu)); 17062306a36Sopenharmony_ci if (old_active_cntx && 17162306a36Sopenharmony_ci ((cntx & ~asid_mask) == atomic_long_read(¤t_version)) && 17262306a36Sopenharmony_ci atomic_long_cmpxchg_relaxed(&per_cpu(active_context, cpu), 17362306a36Sopenharmony_ci old_active_cntx, cntx)) 17462306a36Sopenharmony_ci goto switch_mm_fast; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci raw_spin_lock_irqsave(&context_lock, flags); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci /* Check that our ASID belongs to the current_version. */ 17962306a36Sopenharmony_ci cntx = atomic_long_read(&mm->context.id); 18062306a36Sopenharmony_ci if ((cntx & ~asid_mask) != atomic_long_read(¤t_version)) { 18162306a36Sopenharmony_ci cntx = __new_context(mm); 18262306a36Sopenharmony_ci atomic_long_set(&mm->context.id, cntx); 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci if (cpumask_test_and_clear_cpu(cpu, &context_tlb_flush_pending)) 18662306a36Sopenharmony_ci need_flush_tlb = true; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci atomic_long_set(&per_cpu(active_context, cpu), cntx); 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&context_lock, flags); 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ciswitch_mm_fast: 19362306a36Sopenharmony_ci csr_write(CSR_SATP, virt_to_pfn(mm->pgd) | 19462306a36Sopenharmony_ci ((cntx & asid_mask) << SATP_ASID_SHIFT) | 19562306a36Sopenharmony_ci satp_mode); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci if (need_flush_tlb) 19862306a36Sopenharmony_ci local_flush_tlb_all(); 19962306a36Sopenharmony_ci} 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_cistatic void set_mm_noasid(struct mm_struct *mm) 20262306a36Sopenharmony_ci{ 20362306a36Sopenharmony_ci /* Switch the page table and blindly nuke entire local TLB */ 20462306a36Sopenharmony_ci csr_write(CSR_SATP, virt_to_pfn(mm->pgd) | satp_mode); 20562306a36Sopenharmony_ci local_flush_tlb_all(); 20662306a36Sopenharmony_ci} 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_cistatic inline void set_mm(struct mm_struct *prev, 20962306a36Sopenharmony_ci struct mm_struct *next, unsigned int cpu) 21062306a36Sopenharmony_ci{ 21162306a36Sopenharmony_ci /* 21262306a36Sopenharmony_ci * The mm_cpumask indicates which harts' TLBs contain the virtual 21362306a36Sopenharmony_ci * address mapping of the mm. Compared to noasid, using asid 21462306a36Sopenharmony_ci * can't guarantee that stale TLB entries are invalidated because 21562306a36Sopenharmony_ci * the asid mechanism wouldn't flush TLB for every switch_mm for 21662306a36Sopenharmony_ci * performance. So when using asid, keep all CPUs footmarks in 21762306a36Sopenharmony_ci * cpumask() until mm reset. 21862306a36Sopenharmony_ci */ 21962306a36Sopenharmony_ci cpumask_set_cpu(cpu, mm_cpumask(next)); 22062306a36Sopenharmony_ci if (static_branch_unlikely(&use_asid_allocator)) { 22162306a36Sopenharmony_ci set_mm_asid(next, cpu); 22262306a36Sopenharmony_ci } else { 22362306a36Sopenharmony_ci cpumask_clear_cpu(cpu, mm_cpumask(prev)); 22462306a36Sopenharmony_ci set_mm_noasid(next); 22562306a36Sopenharmony_ci } 22662306a36Sopenharmony_ci} 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_cistatic int __init asids_init(void) 22962306a36Sopenharmony_ci{ 23062306a36Sopenharmony_ci unsigned long old; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci /* Figure-out number of ASID bits in HW */ 23362306a36Sopenharmony_ci old = csr_read(CSR_SATP); 23462306a36Sopenharmony_ci asid_bits = old | (SATP_ASID_MASK << SATP_ASID_SHIFT); 23562306a36Sopenharmony_ci csr_write(CSR_SATP, asid_bits); 23662306a36Sopenharmony_ci asid_bits = (csr_read(CSR_SATP) >> SATP_ASID_SHIFT) & SATP_ASID_MASK; 23762306a36Sopenharmony_ci asid_bits = fls_long(asid_bits); 23862306a36Sopenharmony_ci csr_write(CSR_SATP, old); 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci /* 24162306a36Sopenharmony_ci * In the process of determining number of ASID bits (above) 24262306a36Sopenharmony_ci * we polluted the TLB of current HART so let's do TLB flushed 24362306a36Sopenharmony_ci * to remove unwanted TLB enteries. 24462306a36Sopenharmony_ci */ 24562306a36Sopenharmony_ci local_flush_tlb_all(); 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci /* Pre-compute ASID details */ 24862306a36Sopenharmony_ci if (asid_bits) { 24962306a36Sopenharmony_ci num_asids = 1 << asid_bits; 25062306a36Sopenharmony_ci asid_mask = num_asids - 1; 25162306a36Sopenharmony_ci } 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci /* 25462306a36Sopenharmony_ci * Use ASID allocator only if number of HW ASIDs are 25562306a36Sopenharmony_ci * at-least twice more than CPUs 25662306a36Sopenharmony_ci */ 25762306a36Sopenharmony_ci if (num_asids > (2 * num_possible_cpus())) { 25862306a36Sopenharmony_ci atomic_long_set(¤t_version, num_asids); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci context_asid_map = bitmap_zalloc(num_asids, GFP_KERNEL); 26162306a36Sopenharmony_ci if (!context_asid_map) 26262306a36Sopenharmony_ci panic("Failed to allocate bitmap for %lu ASIDs\n", 26362306a36Sopenharmony_ci num_asids); 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci __set_bit(0, context_asid_map); 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci static_branch_enable(&use_asid_allocator); 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci pr_info("ASID allocator using %lu bits (%lu entries)\n", 27062306a36Sopenharmony_ci asid_bits, num_asids); 27162306a36Sopenharmony_ci } else { 27262306a36Sopenharmony_ci pr_info("ASID allocator disabled (%lu bits)\n", asid_bits); 27362306a36Sopenharmony_ci } 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci return 0; 27662306a36Sopenharmony_ci} 27762306a36Sopenharmony_ciearly_initcall(asids_init); 27862306a36Sopenharmony_ci#else 27962306a36Sopenharmony_cistatic inline void set_mm(struct mm_struct *prev, 28062306a36Sopenharmony_ci struct mm_struct *next, unsigned int cpu) 28162306a36Sopenharmony_ci{ 28262306a36Sopenharmony_ci /* Nothing to do here when there is no MMU */ 28362306a36Sopenharmony_ci} 28462306a36Sopenharmony_ci#endif 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci/* 28762306a36Sopenharmony_ci * When necessary, performs a deferred icache flush for the given MM context, 28862306a36Sopenharmony_ci * on the local CPU. RISC-V has no direct mechanism for instruction cache 28962306a36Sopenharmony_ci * shoot downs, so instead we send an IPI that informs the remote harts they 29062306a36Sopenharmony_ci * need to flush their local instruction caches. To avoid pathologically slow 29162306a36Sopenharmony_ci * behavior in a common case (a bunch of single-hart processes on a many-hart 29262306a36Sopenharmony_ci * machine, ie 'make -j') we avoid the IPIs for harts that are not currently 29362306a36Sopenharmony_ci * executing a MM context and instead schedule a deferred local instruction 29462306a36Sopenharmony_ci * cache flush to be performed before execution resumes on each hart. This 29562306a36Sopenharmony_ci * actually performs that local instruction cache flush, which implicitly only 29662306a36Sopenharmony_ci * refers to the current hart. 29762306a36Sopenharmony_ci * 29862306a36Sopenharmony_ci * The "cpu" argument must be the current local CPU number. 29962306a36Sopenharmony_ci */ 30062306a36Sopenharmony_cistatic inline void flush_icache_deferred(struct mm_struct *mm, unsigned int cpu) 30162306a36Sopenharmony_ci{ 30262306a36Sopenharmony_ci#ifdef CONFIG_SMP 30362306a36Sopenharmony_ci cpumask_t *mask = &mm->context.icache_stale_mask; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci if (cpumask_test_cpu(cpu, mask)) { 30662306a36Sopenharmony_ci cpumask_clear_cpu(cpu, mask); 30762306a36Sopenharmony_ci /* 30862306a36Sopenharmony_ci * Ensure the remote hart's writes are visible to this hart. 30962306a36Sopenharmony_ci * This pairs with a barrier in flush_icache_mm. 31062306a36Sopenharmony_ci */ 31162306a36Sopenharmony_ci smp_mb(); 31262306a36Sopenharmony_ci local_flush_icache_all(); 31362306a36Sopenharmony_ci } 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci#endif 31662306a36Sopenharmony_ci} 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_civoid switch_mm(struct mm_struct *prev, struct mm_struct *next, 31962306a36Sopenharmony_ci struct task_struct *task) 32062306a36Sopenharmony_ci{ 32162306a36Sopenharmony_ci unsigned int cpu; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci if (unlikely(prev == next)) 32462306a36Sopenharmony_ci return; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci /* 32762306a36Sopenharmony_ci * Mark the current MM context as inactive, and the next as 32862306a36Sopenharmony_ci * active. This is at least used by the icache flushing 32962306a36Sopenharmony_ci * routines in order to determine who should be flushed. 33062306a36Sopenharmony_ci */ 33162306a36Sopenharmony_ci cpu = smp_processor_id(); 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci set_mm(prev, next, cpu); 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci flush_icache_deferred(next, cpu); 33662306a36Sopenharmony_ci} 337