18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * TLB Management (flush/create/diagnostics) for ARC700 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * vineetg: Aug 2011 88c2ecf20Sopenharmony_ci * -Reintroduce duplicate PD fixup - some customer chips still have the issue 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * vineetg: May 2011 118c2ecf20Sopenharmony_ci * -No need to flush_cache_page( ) for each call to update_mmu_cache() 128c2ecf20Sopenharmony_ci * some of the LMBench tests improved amazingly 138c2ecf20Sopenharmony_ci * = page-fault thrice as fast (75 usec to 28 usec) 148c2ecf20Sopenharmony_ci * = mmap twice as fast (9.6 msec to 4.6 msec), 158c2ecf20Sopenharmony_ci * = fork (5.3 msec to 3.7 msec) 168c2ecf20Sopenharmony_ci * 178c2ecf20Sopenharmony_ci * vineetg: April 2011 : 188c2ecf20Sopenharmony_ci * -MMU v3: PD{0,1} bits layout changed: They don't overlap anymore, 198c2ecf20Sopenharmony_ci * helps avoid a shift when preparing PD0 from PTE 208c2ecf20Sopenharmony_ci * 218c2ecf20Sopenharmony_ci * vineetg: April 2011 : Preparing for MMU V3 228c2ecf20Sopenharmony_ci * -MMU v2/v3 BCRs decoded differently 238c2ecf20Sopenharmony_ci * -Remove TLB_SIZE hardcoding as it's variable now: 256 or 512 248c2ecf20Sopenharmony_ci * -tlb_entry_erase( ) can be void 258c2ecf20Sopenharmony_ci * -local_flush_tlb_range( ): 268c2ecf20Sopenharmony_ci * = need not "ceil" @end 278c2ecf20Sopenharmony_ci * = walks MMU only if range spans < 32 entries, as opposed to 256 288c2ecf20Sopenharmony_ci * 298c2ecf20Sopenharmony_ci * Vineetg: Sept 10th 2008 308c2ecf20Sopenharmony_ci * -Changes related to MMU v2 (Rel 4.8) 318c2ecf20Sopenharmony_ci * 328c2ecf20Sopenharmony_ci * Vineetg: Aug 29th 2008 338c2ecf20Sopenharmony_ci * -In TLB Flush operations (Metal Fix MMU) there is a explicit command to 348c2ecf20Sopenharmony_ci * flush Micro-TLBS. If TLB Index Reg is invalid prior to TLBIVUTLB cmd, 358c2ecf20Sopenharmony_ci * it fails. Thus need to load it with ANY valid value before invoking 368c2ecf20Sopenharmony_ci * TLBIVUTLB cmd 378c2ecf20Sopenharmony_ci * 388c2ecf20Sopenharmony_ci * Vineetg: Aug 21th 2008: 398c2ecf20Sopenharmony_ci * -Reduced the duration of IRQ lockouts in TLB Flush routines 408c2ecf20Sopenharmony_ci * -Multiple copies of TLB erase code separated into a "single" function 418c2ecf20Sopenharmony_ci * -In TLB Flush routines, interrupt disabling moved UP to retrieve ASID 428c2ecf20Sopenharmony_ci * in interrupt-safe region. 438c2ecf20Sopenharmony_ci * 448c2ecf20Sopenharmony_ci * Vineetg: April 23rd Bug #93131 458c2ecf20Sopenharmony_ci * Problem: tlb_flush_kernel_range() doesn't do anything if the range to 468c2ecf20Sopenharmony_ci * flush is more than the size of TLB itself. 478c2ecf20Sopenharmony_ci * 488c2ecf20Sopenharmony_ci * Rahul Trivedi : Codito Technologies 2004 498c2ecf20Sopenharmony_ci */ 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci#include <linux/module.h> 528c2ecf20Sopenharmony_ci#include <linux/bug.h> 538c2ecf20Sopenharmony_ci#include <linux/mm_types.h> 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci#include <asm/arcregs.h> 568c2ecf20Sopenharmony_ci#include <asm/setup.h> 578c2ecf20Sopenharmony_ci#include <asm/mmu_context.h> 588c2ecf20Sopenharmony_ci#include <asm/mmu.h> 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci/* Need for ARC MMU v2 618c2ecf20Sopenharmony_ci * 628c2ecf20Sopenharmony_ci * ARC700 MMU-v1 had a Joint-TLB for Code and Data and is 2 way set-assoc. 638c2ecf20Sopenharmony_ci * For a memcpy operation with 3 players (src/dst/code) such that all 3 pages 648c2ecf20Sopenharmony_ci * map into same set, there would be contention for the 2 ways causing severe 658c2ecf20Sopenharmony_ci * Thrashing. 668c2ecf20Sopenharmony_ci * 678c2ecf20Sopenharmony_ci * Although J-TLB is 2 way set assoc, ARC700 caches J-TLB into uTLBS which has 688c2ecf20Sopenharmony_ci * much higher associativity. u-D-TLB is 8 ways, u-I-TLB is 4 ways. 698c2ecf20Sopenharmony_ci * Given this, the thrashing problem should never happen because once the 3 708c2ecf20Sopenharmony_ci * J-TLB entries are created (even though 3rd will knock out one of the prev 718c2ecf20Sopenharmony_ci * two), the u-D-TLB and u-I-TLB will have what is required to accomplish memcpy 728c2ecf20Sopenharmony_ci * 738c2ecf20Sopenharmony_ci * Yet we still see the Thrashing because a J-TLB Write cause flush of u-TLBs. 748c2ecf20Sopenharmony_ci * This is a simple design for keeping them in sync. So what do we do? 758c2ecf20Sopenharmony_ci * The solution which James came up was pretty neat. It utilised the assoc 768c2ecf20Sopenharmony_ci * of uTLBs by not invalidating always but only when absolutely necessary. 778c2ecf20Sopenharmony_ci * 788c2ecf20Sopenharmony_ci * - Existing TLB commands work as before 798c2ecf20Sopenharmony_ci * - New command (TLBWriteNI) for TLB write without clearing uTLBs 808c2ecf20Sopenharmony_ci * - New command (TLBIVUTLB) to invalidate uTLBs. 818c2ecf20Sopenharmony_ci * 828c2ecf20Sopenharmony_ci * The uTLBs need only be invalidated when pages are being removed from the 838c2ecf20Sopenharmony_ci * OS page table. If a 'victim' TLB entry is being overwritten in the main TLB 848c2ecf20Sopenharmony_ci * as a result of a miss, the removed entry is still allowed to exist in the 858c2ecf20Sopenharmony_ci * uTLBs as it is still valid and present in the OS page table. This allows the 868c2ecf20Sopenharmony_ci * full associativity of the uTLBs to hide the limited associativity of the main 878c2ecf20Sopenharmony_ci * TLB. 888c2ecf20Sopenharmony_ci * 898c2ecf20Sopenharmony_ci * During a miss handler, the new "TLBWriteNI" command is used to load 908c2ecf20Sopenharmony_ci * entries without clearing the uTLBs. 918c2ecf20Sopenharmony_ci * 928c2ecf20Sopenharmony_ci * When the OS page table is updated, TLB entries that may be associated with a 938c2ecf20Sopenharmony_ci * removed page are removed (flushed) from the TLB using TLBWrite. In this 948c2ecf20Sopenharmony_ci * circumstance, the uTLBs must also be cleared. This is done by using the 958c2ecf20Sopenharmony_ci * existing TLBWrite command. An explicit IVUTLB is also required for those 968c2ecf20Sopenharmony_ci * corner cases when TLBWrite was not executed at all because the corresp 978c2ecf20Sopenharmony_ci * J-TLB entry got evicted/replaced. 988c2ecf20Sopenharmony_ci */ 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci/* A copy of the ASID from the PID reg is kept in asid_cache */ 1028c2ecf20Sopenharmony_ciDEFINE_PER_CPU(unsigned int, asid_cache) = MM_CTXT_FIRST_CYCLE; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistatic int __read_mostly pae_exists; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci/* 1078c2ecf20Sopenharmony_ci * Utility Routine to erase a J-TLB entry 1088c2ecf20Sopenharmony_ci * Caller needs to setup Index Reg (manually or via getIndex) 1098c2ecf20Sopenharmony_ci */ 1108c2ecf20Sopenharmony_cistatic inline void __tlb_entry_erase(void) 1118c2ecf20Sopenharmony_ci{ 1128c2ecf20Sopenharmony_ci write_aux_reg(ARC_REG_TLBPD1, 0); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci if (is_pae40_enabled()) 1158c2ecf20Sopenharmony_ci write_aux_reg(ARC_REG_TLBPD1HI, 0); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci write_aux_reg(ARC_REG_TLBPD0, 0); 1188c2ecf20Sopenharmony_ci write_aux_reg(ARC_REG_TLBCOMMAND, TLBWrite); 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistatic void utlb_invalidate(void) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci#if (CONFIG_ARC_MMU_VER >= 2) 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci#if (CONFIG_ARC_MMU_VER == 2) 1268c2ecf20Sopenharmony_ci /* MMU v2 introduced the uTLB Flush command. 1278c2ecf20Sopenharmony_ci * There was however an obscure hardware bug, where uTLB flush would 1288c2ecf20Sopenharmony_ci * fail when a prior probe for J-TLB (both totally unrelated) would 1298c2ecf20Sopenharmony_ci * return lkup err - because the entry didn't exist in MMU. 1308c2ecf20Sopenharmony_ci * The Workaround was to set Index reg with some valid value, prior to 1318c2ecf20Sopenharmony_ci * flush. This was fixed in MMU v3 1328c2ecf20Sopenharmony_ci */ 1338c2ecf20Sopenharmony_ci unsigned int idx; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci /* make sure INDEX Reg is valid */ 1368c2ecf20Sopenharmony_ci idx = read_aux_reg(ARC_REG_TLBINDEX); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci /* If not write some dummy val */ 1398c2ecf20Sopenharmony_ci if (unlikely(idx & TLB_LKUP_ERR)) 1408c2ecf20Sopenharmony_ci write_aux_reg(ARC_REG_TLBINDEX, 0xa); 1418c2ecf20Sopenharmony_ci#endif 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci write_aux_reg(ARC_REG_TLBCOMMAND, TLBIVUTLB); 1448c2ecf20Sopenharmony_ci#endif 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci#if (CONFIG_ARC_MMU_VER < 4) 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistatic inline unsigned int tlb_entry_lkup(unsigned long vaddr_n_asid) 1518c2ecf20Sopenharmony_ci{ 1528c2ecf20Sopenharmony_ci unsigned int idx; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci write_aux_reg(ARC_REG_TLBPD0, vaddr_n_asid); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci write_aux_reg(ARC_REG_TLBCOMMAND, TLBProbe); 1578c2ecf20Sopenharmony_ci idx = read_aux_reg(ARC_REG_TLBINDEX); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci return idx; 1608c2ecf20Sopenharmony_ci} 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_cistatic void tlb_entry_erase(unsigned int vaddr_n_asid) 1638c2ecf20Sopenharmony_ci{ 1648c2ecf20Sopenharmony_ci unsigned int idx; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci /* Locate the TLB entry for this vaddr + ASID */ 1678c2ecf20Sopenharmony_ci idx = tlb_entry_lkup(vaddr_n_asid); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci /* No error means entry found, zero it out */ 1708c2ecf20Sopenharmony_ci if (likely(!(idx & TLB_LKUP_ERR))) { 1718c2ecf20Sopenharmony_ci __tlb_entry_erase(); 1728c2ecf20Sopenharmony_ci } else { 1738c2ecf20Sopenharmony_ci /* Duplicate entry error */ 1748c2ecf20Sopenharmony_ci WARN(idx == TLB_DUP_ERR, "Probe returned Dup PD for %x\n", 1758c2ecf20Sopenharmony_ci vaddr_n_asid); 1768c2ecf20Sopenharmony_ci } 1778c2ecf20Sopenharmony_ci} 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_cistatic void tlb_entry_insert(unsigned int pd0, pte_t pd1) 1808c2ecf20Sopenharmony_ci{ 1818c2ecf20Sopenharmony_ci unsigned int idx; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci /* 1848c2ecf20Sopenharmony_ci * First verify if entry for this vaddr+ASID already exists 1858c2ecf20Sopenharmony_ci * This also sets up PD0 (vaddr, ASID..) for final commit 1868c2ecf20Sopenharmony_ci */ 1878c2ecf20Sopenharmony_ci idx = tlb_entry_lkup(pd0); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci /* 1908c2ecf20Sopenharmony_ci * If Not already present get a free slot from MMU. 1918c2ecf20Sopenharmony_ci * Otherwise, Probe would have located the entry and set INDEX Reg 1928c2ecf20Sopenharmony_ci * with existing location. This will cause Write CMD to over-write 1938c2ecf20Sopenharmony_ci * existing entry with new PD0 and PD1 1948c2ecf20Sopenharmony_ci */ 1958c2ecf20Sopenharmony_ci if (likely(idx & TLB_LKUP_ERR)) 1968c2ecf20Sopenharmony_ci write_aux_reg(ARC_REG_TLBCOMMAND, TLBGetIndex); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci /* setup the other half of TLB entry (pfn, rwx..) */ 1998c2ecf20Sopenharmony_ci write_aux_reg(ARC_REG_TLBPD1, pd1); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci /* 2028c2ecf20Sopenharmony_ci * Commit the Entry to MMU 2038c2ecf20Sopenharmony_ci * It doesn't sound safe to use the TLBWriteNI cmd here 2048c2ecf20Sopenharmony_ci * which doesn't flush uTLBs. I'd rather be safe than sorry. 2058c2ecf20Sopenharmony_ci */ 2068c2ecf20Sopenharmony_ci write_aux_reg(ARC_REG_TLBCOMMAND, TLBWrite); 2078c2ecf20Sopenharmony_ci} 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci#else /* CONFIG_ARC_MMU_VER >= 4) */ 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_cistatic void tlb_entry_erase(unsigned int vaddr_n_asid) 2128c2ecf20Sopenharmony_ci{ 2138c2ecf20Sopenharmony_ci write_aux_reg(ARC_REG_TLBPD0, vaddr_n_asid | _PAGE_PRESENT); 2148c2ecf20Sopenharmony_ci write_aux_reg(ARC_REG_TLBCOMMAND, TLBDeleteEntry); 2158c2ecf20Sopenharmony_ci} 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_cistatic void tlb_entry_insert(unsigned int pd0, pte_t pd1) 2188c2ecf20Sopenharmony_ci{ 2198c2ecf20Sopenharmony_ci write_aux_reg(ARC_REG_TLBPD0, pd0); 2208c2ecf20Sopenharmony_ci write_aux_reg(ARC_REG_TLBPD1, pd1); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci if (is_pae40_enabled()) 2238c2ecf20Sopenharmony_ci write_aux_reg(ARC_REG_TLBPD1HI, (u64)pd1 >> 32); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci write_aux_reg(ARC_REG_TLBCOMMAND, TLBInsertEntry); 2268c2ecf20Sopenharmony_ci} 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci#endif 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci/* 2318c2ecf20Sopenharmony_ci * Un-conditionally (without lookup) erase the entire MMU contents 2328c2ecf20Sopenharmony_ci */ 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_cinoinline void local_flush_tlb_all(void) 2358c2ecf20Sopenharmony_ci{ 2368c2ecf20Sopenharmony_ci struct cpuinfo_arc_mmu *mmu = &cpuinfo_arc700[smp_processor_id()].mmu; 2378c2ecf20Sopenharmony_ci unsigned long flags; 2388c2ecf20Sopenharmony_ci unsigned int entry; 2398c2ecf20Sopenharmony_ci int num_tlb = mmu->sets * mmu->ways; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci local_irq_save(flags); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci /* Load PD0 and PD1 with template for a Blank Entry */ 2448c2ecf20Sopenharmony_ci write_aux_reg(ARC_REG_TLBPD1, 0); 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci if (is_pae40_enabled()) 2478c2ecf20Sopenharmony_ci write_aux_reg(ARC_REG_TLBPD1HI, 0); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci write_aux_reg(ARC_REG_TLBPD0, 0); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci for (entry = 0; entry < num_tlb; entry++) { 2528c2ecf20Sopenharmony_ci /* write this entry to the TLB */ 2538c2ecf20Sopenharmony_ci write_aux_reg(ARC_REG_TLBINDEX, entry); 2548c2ecf20Sopenharmony_ci write_aux_reg(ARC_REG_TLBCOMMAND, TLBWriteNI); 2558c2ecf20Sopenharmony_ci } 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE)) { 2588c2ecf20Sopenharmony_ci const int stlb_idx = 0x800; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci /* Blank sTLB entry */ 2618c2ecf20Sopenharmony_ci write_aux_reg(ARC_REG_TLBPD0, _PAGE_HW_SZ); 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci for (entry = stlb_idx; entry < stlb_idx + 16; entry++) { 2648c2ecf20Sopenharmony_ci write_aux_reg(ARC_REG_TLBINDEX, entry); 2658c2ecf20Sopenharmony_ci write_aux_reg(ARC_REG_TLBCOMMAND, TLBWriteNI); 2668c2ecf20Sopenharmony_ci } 2678c2ecf20Sopenharmony_ci } 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci utlb_invalidate(); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci local_irq_restore(flags); 2728c2ecf20Sopenharmony_ci} 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci/* 2758c2ecf20Sopenharmony_ci * Flush the entire MM for userland. The fastest way is to move to Next ASID 2768c2ecf20Sopenharmony_ci */ 2778c2ecf20Sopenharmony_cinoinline void local_flush_tlb_mm(struct mm_struct *mm) 2788c2ecf20Sopenharmony_ci{ 2798c2ecf20Sopenharmony_ci /* 2808c2ecf20Sopenharmony_ci * Small optimisation courtesy IA64 2818c2ecf20Sopenharmony_ci * flush_mm called during fork,exit,munmap etc, multiple times as well. 2828c2ecf20Sopenharmony_ci * Only for fork( ) do we need to move parent to a new MMU ctxt, 2838c2ecf20Sopenharmony_ci * all other cases are NOPs, hence this check. 2848c2ecf20Sopenharmony_ci */ 2858c2ecf20Sopenharmony_ci if (atomic_read(&mm->mm_users) == 0) 2868c2ecf20Sopenharmony_ci return; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci /* 2898c2ecf20Sopenharmony_ci * - Move to a new ASID, but only if the mm is still wired in 2908c2ecf20Sopenharmony_ci * (Android Binder ended up calling this for vma->mm != tsk->mm, 2918c2ecf20Sopenharmony_ci * causing h/w - s/w ASID to get out of sync) 2928c2ecf20Sopenharmony_ci * - Also get_new_mmu_context() new implementation allocates a new 2938c2ecf20Sopenharmony_ci * ASID only if it is not allocated already - so unallocate first 2948c2ecf20Sopenharmony_ci */ 2958c2ecf20Sopenharmony_ci destroy_context(mm); 2968c2ecf20Sopenharmony_ci if (current->mm == mm) 2978c2ecf20Sopenharmony_ci get_new_mmu_context(mm); 2988c2ecf20Sopenharmony_ci} 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci/* 3018c2ecf20Sopenharmony_ci * Flush a Range of TLB entries for userland. 3028c2ecf20Sopenharmony_ci * @start is inclusive, while @end is exclusive 3038c2ecf20Sopenharmony_ci * Difference between this and Kernel Range Flush is 3048c2ecf20Sopenharmony_ci * -Here the fastest way (if range is too large) is to move to next ASID 3058c2ecf20Sopenharmony_ci * without doing any explicit Shootdown 3068c2ecf20Sopenharmony_ci * -In case of kernel Flush, entry has to be shot down explicitly 3078c2ecf20Sopenharmony_ci */ 3088c2ecf20Sopenharmony_civoid local_flush_tlb_range(struct vm_area_struct *vma, unsigned long start, 3098c2ecf20Sopenharmony_ci unsigned long end) 3108c2ecf20Sopenharmony_ci{ 3118c2ecf20Sopenharmony_ci const unsigned int cpu = smp_processor_id(); 3128c2ecf20Sopenharmony_ci unsigned long flags; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci /* If range @start to @end is more than 32 TLB entries deep, 3158c2ecf20Sopenharmony_ci * its better to move to a new ASID rather than searching for 3168c2ecf20Sopenharmony_ci * individual entries and then shooting them down 3178c2ecf20Sopenharmony_ci * 3188c2ecf20Sopenharmony_ci * The calc above is rough, doesn't account for unaligned parts, 3198c2ecf20Sopenharmony_ci * since this is heuristics based anyways 3208c2ecf20Sopenharmony_ci */ 3218c2ecf20Sopenharmony_ci if (unlikely((end - start) >= PAGE_SIZE * 32)) { 3228c2ecf20Sopenharmony_ci local_flush_tlb_mm(vma->vm_mm); 3238c2ecf20Sopenharmony_ci return; 3248c2ecf20Sopenharmony_ci } 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci /* 3278c2ecf20Sopenharmony_ci * @start moved to page start: this alone suffices for checking 3288c2ecf20Sopenharmony_ci * loop end condition below, w/o need for aligning @end to end 3298c2ecf20Sopenharmony_ci * e.g. 2000 to 4001 will anyhow loop twice 3308c2ecf20Sopenharmony_ci */ 3318c2ecf20Sopenharmony_ci start &= PAGE_MASK; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci local_irq_save(flags); 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci if (asid_mm(vma->vm_mm, cpu) != MM_CTXT_NO_ASID) { 3368c2ecf20Sopenharmony_ci while (start < end) { 3378c2ecf20Sopenharmony_ci tlb_entry_erase(start | hw_pid(vma->vm_mm, cpu)); 3388c2ecf20Sopenharmony_ci start += PAGE_SIZE; 3398c2ecf20Sopenharmony_ci } 3408c2ecf20Sopenharmony_ci } 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci local_irq_restore(flags); 3438c2ecf20Sopenharmony_ci} 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci/* Flush the kernel TLB entries - vmalloc/modules (Global from MMU perspective) 3468c2ecf20Sopenharmony_ci * @start, @end interpreted as kvaddr 3478c2ecf20Sopenharmony_ci * Interestingly, shared TLB entries can also be flushed using just 3488c2ecf20Sopenharmony_ci * @start,@end alone (interpreted as user vaddr), although technically SASID 3498c2ecf20Sopenharmony_ci * is also needed. However our smart TLbProbe lookup takes care of that. 3508c2ecf20Sopenharmony_ci */ 3518c2ecf20Sopenharmony_civoid local_flush_tlb_kernel_range(unsigned long start, unsigned long end) 3528c2ecf20Sopenharmony_ci{ 3538c2ecf20Sopenharmony_ci unsigned long flags; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci /* exactly same as above, except for TLB entry not taking ASID */ 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci if (unlikely((end - start) >= PAGE_SIZE * 32)) { 3588c2ecf20Sopenharmony_ci local_flush_tlb_all(); 3598c2ecf20Sopenharmony_ci return; 3608c2ecf20Sopenharmony_ci } 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci start &= PAGE_MASK; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci local_irq_save(flags); 3658c2ecf20Sopenharmony_ci while (start < end) { 3668c2ecf20Sopenharmony_ci tlb_entry_erase(start); 3678c2ecf20Sopenharmony_ci start += PAGE_SIZE; 3688c2ecf20Sopenharmony_ci } 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci local_irq_restore(flags); 3718c2ecf20Sopenharmony_ci} 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci/* 3748c2ecf20Sopenharmony_ci * Delete TLB entry in MMU for a given page (??? address) 3758c2ecf20Sopenharmony_ci * NOTE One TLB entry contains translation for single PAGE 3768c2ecf20Sopenharmony_ci */ 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_civoid local_flush_tlb_page(struct vm_area_struct *vma, unsigned long page) 3798c2ecf20Sopenharmony_ci{ 3808c2ecf20Sopenharmony_ci const unsigned int cpu = smp_processor_id(); 3818c2ecf20Sopenharmony_ci unsigned long flags; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci /* Note that it is critical that interrupts are DISABLED between 3848c2ecf20Sopenharmony_ci * checking the ASID and using it flush the TLB entry 3858c2ecf20Sopenharmony_ci */ 3868c2ecf20Sopenharmony_ci local_irq_save(flags); 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci if (asid_mm(vma->vm_mm, cpu) != MM_CTXT_NO_ASID) { 3898c2ecf20Sopenharmony_ci tlb_entry_erase((page & PAGE_MASK) | hw_pid(vma->vm_mm, cpu)); 3908c2ecf20Sopenharmony_ci } 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci local_irq_restore(flags); 3938c2ecf20Sopenharmony_ci} 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci#ifdef CONFIG_SMP 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_cistruct tlb_args { 3988c2ecf20Sopenharmony_ci struct vm_area_struct *ta_vma; 3998c2ecf20Sopenharmony_ci unsigned long ta_start; 4008c2ecf20Sopenharmony_ci unsigned long ta_end; 4018c2ecf20Sopenharmony_ci}; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_cistatic inline void ipi_flush_tlb_page(void *arg) 4048c2ecf20Sopenharmony_ci{ 4058c2ecf20Sopenharmony_ci struct tlb_args *ta = arg; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci local_flush_tlb_page(ta->ta_vma, ta->ta_start); 4088c2ecf20Sopenharmony_ci} 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_cistatic inline void ipi_flush_tlb_range(void *arg) 4118c2ecf20Sopenharmony_ci{ 4128c2ecf20Sopenharmony_ci struct tlb_args *ta = arg; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci local_flush_tlb_range(ta->ta_vma, ta->ta_start, ta->ta_end); 4158c2ecf20Sopenharmony_ci} 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci#ifdef CONFIG_TRANSPARENT_HUGEPAGE 4188c2ecf20Sopenharmony_cistatic inline void ipi_flush_pmd_tlb_range(void *arg) 4198c2ecf20Sopenharmony_ci{ 4208c2ecf20Sopenharmony_ci struct tlb_args *ta = arg; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci local_flush_pmd_tlb_range(ta->ta_vma, ta->ta_start, ta->ta_end); 4238c2ecf20Sopenharmony_ci} 4248c2ecf20Sopenharmony_ci#endif 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_cistatic inline void ipi_flush_tlb_kernel_range(void *arg) 4278c2ecf20Sopenharmony_ci{ 4288c2ecf20Sopenharmony_ci struct tlb_args *ta = (struct tlb_args *)arg; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci local_flush_tlb_kernel_range(ta->ta_start, ta->ta_end); 4318c2ecf20Sopenharmony_ci} 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_civoid flush_tlb_all(void) 4348c2ecf20Sopenharmony_ci{ 4358c2ecf20Sopenharmony_ci on_each_cpu((smp_call_func_t)local_flush_tlb_all, NULL, 1); 4368c2ecf20Sopenharmony_ci} 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_civoid flush_tlb_mm(struct mm_struct *mm) 4398c2ecf20Sopenharmony_ci{ 4408c2ecf20Sopenharmony_ci on_each_cpu_mask(mm_cpumask(mm), (smp_call_func_t)local_flush_tlb_mm, 4418c2ecf20Sopenharmony_ci mm, 1); 4428c2ecf20Sopenharmony_ci} 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_civoid flush_tlb_page(struct vm_area_struct *vma, unsigned long uaddr) 4458c2ecf20Sopenharmony_ci{ 4468c2ecf20Sopenharmony_ci struct tlb_args ta = { 4478c2ecf20Sopenharmony_ci .ta_vma = vma, 4488c2ecf20Sopenharmony_ci .ta_start = uaddr 4498c2ecf20Sopenharmony_ci }; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci on_each_cpu_mask(mm_cpumask(vma->vm_mm), ipi_flush_tlb_page, &ta, 1); 4528c2ecf20Sopenharmony_ci} 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_civoid flush_tlb_range(struct vm_area_struct *vma, unsigned long start, 4558c2ecf20Sopenharmony_ci unsigned long end) 4568c2ecf20Sopenharmony_ci{ 4578c2ecf20Sopenharmony_ci struct tlb_args ta = { 4588c2ecf20Sopenharmony_ci .ta_vma = vma, 4598c2ecf20Sopenharmony_ci .ta_start = start, 4608c2ecf20Sopenharmony_ci .ta_end = end 4618c2ecf20Sopenharmony_ci }; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci on_each_cpu_mask(mm_cpumask(vma->vm_mm), ipi_flush_tlb_range, &ta, 1); 4648c2ecf20Sopenharmony_ci} 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci#ifdef CONFIG_TRANSPARENT_HUGEPAGE 4678c2ecf20Sopenharmony_civoid flush_pmd_tlb_range(struct vm_area_struct *vma, unsigned long start, 4688c2ecf20Sopenharmony_ci unsigned long end) 4698c2ecf20Sopenharmony_ci{ 4708c2ecf20Sopenharmony_ci struct tlb_args ta = { 4718c2ecf20Sopenharmony_ci .ta_vma = vma, 4728c2ecf20Sopenharmony_ci .ta_start = start, 4738c2ecf20Sopenharmony_ci .ta_end = end 4748c2ecf20Sopenharmony_ci }; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci on_each_cpu_mask(mm_cpumask(vma->vm_mm), ipi_flush_pmd_tlb_range, &ta, 1); 4778c2ecf20Sopenharmony_ci} 4788c2ecf20Sopenharmony_ci#endif 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_civoid flush_tlb_kernel_range(unsigned long start, unsigned long end) 4818c2ecf20Sopenharmony_ci{ 4828c2ecf20Sopenharmony_ci struct tlb_args ta = { 4838c2ecf20Sopenharmony_ci .ta_start = start, 4848c2ecf20Sopenharmony_ci .ta_end = end 4858c2ecf20Sopenharmony_ci }; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci on_each_cpu(ipi_flush_tlb_kernel_range, &ta, 1); 4888c2ecf20Sopenharmony_ci} 4898c2ecf20Sopenharmony_ci#endif 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci/* 4928c2ecf20Sopenharmony_ci * Routine to create a TLB entry 4938c2ecf20Sopenharmony_ci */ 4948c2ecf20Sopenharmony_civoid create_tlb(struct vm_area_struct *vma, unsigned long vaddr, pte_t *ptep) 4958c2ecf20Sopenharmony_ci{ 4968c2ecf20Sopenharmony_ci unsigned long flags; 4978c2ecf20Sopenharmony_ci unsigned int asid_or_sasid, rwx; 4988c2ecf20Sopenharmony_ci unsigned long pd0; 4998c2ecf20Sopenharmony_ci pte_t pd1; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci /* 5028c2ecf20Sopenharmony_ci * create_tlb() assumes that current->mm == vma->mm, since 5038c2ecf20Sopenharmony_ci * -it ASID for TLB entry is fetched from MMU ASID reg (valid for curr) 5048c2ecf20Sopenharmony_ci * -completes the lazy write to SASID reg (again valid for curr tsk) 5058c2ecf20Sopenharmony_ci * 5068c2ecf20Sopenharmony_ci * Removing the assumption involves 5078c2ecf20Sopenharmony_ci * -Using vma->mm->context{ASID,SASID}, as opposed to MMU reg. 5088c2ecf20Sopenharmony_ci * -Fix the TLB paranoid debug code to not trigger false negatives. 5098c2ecf20Sopenharmony_ci * -More importantly it makes this handler inconsistent with fast-path 5108c2ecf20Sopenharmony_ci * TLB Refill handler which always deals with "current" 5118c2ecf20Sopenharmony_ci * 5128c2ecf20Sopenharmony_ci * Lets see the use cases when current->mm != vma->mm and we land here 5138c2ecf20Sopenharmony_ci * 1. execve->copy_strings()->__get_user_pages->handle_mm_fault 5148c2ecf20Sopenharmony_ci * Here VM wants to pre-install a TLB entry for user stack while 5158c2ecf20Sopenharmony_ci * current->mm still points to pre-execve mm (hence the condition). 5168c2ecf20Sopenharmony_ci * However the stack vaddr is soon relocated (randomization) and 5178c2ecf20Sopenharmony_ci * move_page_tables() tries to undo that TLB entry. 5188c2ecf20Sopenharmony_ci * Thus not creating TLB entry is not any worse. 5198c2ecf20Sopenharmony_ci * 5208c2ecf20Sopenharmony_ci * 2. ptrace(POKETEXT) causes a CoW - debugger(current) inserting a 5218c2ecf20Sopenharmony_ci * breakpoint in debugged task. Not creating a TLB now is not 5228c2ecf20Sopenharmony_ci * performance critical. 5238c2ecf20Sopenharmony_ci * 5248c2ecf20Sopenharmony_ci * Both the cases above are not good enough for code churn. 5258c2ecf20Sopenharmony_ci */ 5268c2ecf20Sopenharmony_ci if (current->active_mm != vma->vm_mm) 5278c2ecf20Sopenharmony_ci return; 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci local_irq_save(flags); 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci tlb_paranoid_check(asid_mm(vma->vm_mm, smp_processor_id()), vaddr); 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci vaddr &= PAGE_MASK; 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci /* update this PTE credentials */ 5368c2ecf20Sopenharmony_ci pte_val(*ptep) |= (_PAGE_PRESENT | _PAGE_ACCESSED); 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci /* Create HW TLB(PD0,PD1) from PTE */ 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci /* ASID for this task */ 5418c2ecf20Sopenharmony_ci asid_or_sasid = read_aux_reg(ARC_REG_PID) & 0xff; 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci pd0 = vaddr | asid_or_sasid | (pte_val(*ptep) & PTE_BITS_IN_PD0); 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci /* 5468c2ecf20Sopenharmony_ci * ARC MMU provides fully orthogonal access bits for K/U mode, 5478c2ecf20Sopenharmony_ci * however Linux only saves 1 set to save PTE real-estate 5488c2ecf20Sopenharmony_ci * Here we convert 3 PTE bits into 6 MMU bits: 5498c2ecf20Sopenharmony_ci * -Kernel only entries have Kr Kw Kx 0 0 0 5508c2ecf20Sopenharmony_ci * -User entries have mirrored K and U bits 5518c2ecf20Sopenharmony_ci */ 5528c2ecf20Sopenharmony_ci rwx = pte_val(*ptep) & PTE_BITS_RWX; 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci if (pte_val(*ptep) & _PAGE_GLOBAL) 5558c2ecf20Sopenharmony_ci rwx <<= 3; /* r w x => Kr Kw Kx 0 0 0 */ 5568c2ecf20Sopenharmony_ci else 5578c2ecf20Sopenharmony_ci rwx |= (rwx << 3); /* r w x => Kr Kw Kx Ur Uw Ux */ 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci pd1 = rwx | (pte_val(*ptep) & PTE_BITS_NON_RWX_IN_PD1); 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci tlb_entry_insert(pd0, pd1); 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci local_irq_restore(flags); 5648c2ecf20Sopenharmony_ci} 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci/* 5678c2ecf20Sopenharmony_ci * Called at the end of pagefault, for a userspace mapped page 5688c2ecf20Sopenharmony_ci * -pre-install the corresponding TLB entry into MMU 5698c2ecf20Sopenharmony_ci * -Finalize the delayed D-cache flush of kernel mapping of page due to 5708c2ecf20Sopenharmony_ci * flush_dcache_page(), copy_user_page() 5718c2ecf20Sopenharmony_ci * 5728c2ecf20Sopenharmony_ci * Note that flush (when done) involves both WBACK - so physical page is 5738c2ecf20Sopenharmony_ci * in sync as well as INV - so any non-congruent aliases don't remain 5748c2ecf20Sopenharmony_ci */ 5758c2ecf20Sopenharmony_civoid update_mmu_cache(struct vm_area_struct *vma, unsigned long vaddr_unaligned, 5768c2ecf20Sopenharmony_ci pte_t *ptep) 5778c2ecf20Sopenharmony_ci{ 5788c2ecf20Sopenharmony_ci unsigned long vaddr = vaddr_unaligned & PAGE_MASK; 5798c2ecf20Sopenharmony_ci phys_addr_t paddr = pte_val(*ptep) & PAGE_MASK_PHYS; 5808c2ecf20Sopenharmony_ci struct page *page = pfn_to_page(pte_pfn(*ptep)); 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci create_tlb(vma, vaddr, ptep); 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci if (page == ZERO_PAGE(0)) { 5858c2ecf20Sopenharmony_ci return; 5868c2ecf20Sopenharmony_ci } 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci /* 5898c2ecf20Sopenharmony_ci * Exec page : Independent of aliasing/page-color considerations, 5908c2ecf20Sopenharmony_ci * since icache doesn't snoop dcache on ARC, any dirty 5918c2ecf20Sopenharmony_ci * K-mapping of a code page needs to be wback+inv so that 5928c2ecf20Sopenharmony_ci * icache fetch by userspace sees code correctly. 5938c2ecf20Sopenharmony_ci * !EXEC page: If K-mapping is NOT congruent to U-mapping, flush it 5948c2ecf20Sopenharmony_ci * so userspace sees the right data. 5958c2ecf20Sopenharmony_ci * (Avoids the flush for Non-exec + congruent mapping case) 5968c2ecf20Sopenharmony_ci */ 5978c2ecf20Sopenharmony_ci if ((vma->vm_flags & VM_EXEC) || 5988c2ecf20Sopenharmony_ci addr_not_cache_congruent(paddr, vaddr)) { 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci int dirty = !test_and_set_bit(PG_dc_clean, &page->flags); 6018c2ecf20Sopenharmony_ci if (dirty) { 6028c2ecf20Sopenharmony_ci /* wback + inv dcache lines (K-mapping) */ 6038c2ecf20Sopenharmony_ci __flush_dcache_page(paddr, paddr); 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci /* invalidate any existing icache lines (U-mapping) */ 6068c2ecf20Sopenharmony_ci if (vma->vm_flags & VM_EXEC) 6078c2ecf20Sopenharmony_ci __inv_icache_page(paddr, vaddr); 6088c2ecf20Sopenharmony_ci } 6098c2ecf20Sopenharmony_ci } 6108c2ecf20Sopenharmony_ci} 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci#ifdef CONFIG_TRANSPARENT_HUGEPAGE 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci/* 6158c2ecf20Sopenharmony_ci * MMUv4 in HS38x cores supports Super Pages which are basis for Linux THP 6168c2ecf20Sopenharmony_ci * support. 6178c2ecf20Sopenharmony_ci * 6188c2ecf20Sopenharmony_ci * Normal and Super pages can co-exist (ofcourse not overlap) in TLB with a 6198c2ecf20Sopenharmony_ci * new bit "SZ" in TLB page descriptor to distinguish between them. 6208c2ecf20Sopenharmony_ci * Super Page size is configurable in hardware (4K to 16M), but fixed once 6218c2ecf20Sopenharmony_ci * RTL builds. 6228c2ecf20Sopenharmony_ci * 6238c2ecf20Sopenharmony_ci * The exact THP size a Linux configuration will support is a function of: 6248c2ecf20Sopenharmony_ci * - MMU page size (typical 8K, RTL fixed) 6258c2ecf20Sopenharmony_ci * - software page walker address split between PGD:PTE:PFN (typical 6268c2ecf20Sopenharmony_ci * 11:8:13, but can be changed with 1 line) 6278c2ecf20Sopenharmony_ci * So for above default, THP size supported is 8K * (2^8) = 2M 6288c2ecf20Sopenharmony_ci * 6298c2ecf20Sopenharmony_ci * Default Page Walker is 2 levels, PGD:PTE:PFN, which in THP regime 6308c2ecf20Sopenharmony_ci * reduces to 1 level (as PTE is folded into PGD and canonically referred 6318c2ecf20Sopenharmony_ci * to as PMD). 6328c2ecf20Sopenharmony_ci * Thus THP PMD accessors are implemented in terms of PTE (just like sparc) 6338c2ecf20Sopenharmony_ci */ 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_civoid update_mmu_cache_pmd(struct vm_area_struct *vma, unsigned long addr, 6368c2ecf20Sopenharmony_ci pmd_t *pmd) 6378c2ecf20Sopenharmony_ci{ 6388c2ecf20Sopenharmony_ci pte_t pte = __pte(pmd_val(*pmd)); 6398c2ecf20Sopenharmony_ci update_mmu_cache(vma, addr, &pte); 6408c2ecf20Sopenharmony_ci} 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_civoid pgtable_trans_huge_deposit(struct mm_struct *mm, pmd_t *pmdp, 6438c2ecf20Sopenharmony_ci pgtable_t pgtable) 6448c2ecf20Sopenharmony_ci{ 6458c2ecf20Sopenharmony_ci struct list_head *lh = (struct list_head *) pgtable; 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci assert_spin_locked(&mm->page_table_lock); 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci /* FIFO */ 6508c2ecf20Sopenharmony_ci if (!pmd_huge_pte(mm, pmdp)) 6518c2ecf20Sopenharmony_ci INIT_LIST_HEAD(lh); 6528c2ecf20Sopenharmony_ci else 6538c2ecf20Sopenharmony_ci list_add(lh, (struct list_head *) pmd_huge_pte(mm, pmdp)); 6548c2ecf20Sopenharmony_ci pmd_huge_pte(mm, pmdp) = pgtable; 6558c2ecf20Sopenharmony_ci} 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_cipgtable_t pgtable_trans_huge_withdraw(struct mm_struct *mm, pmd_t *pmdp) 6588c2ecf20Sopenharmony_ci{ 6598c2ecf20Sopenharmony_ci struct list_head *lh; 6608c2ecf20Sopenharmony_ci pgtable_t pgtable; 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci assert_spin_locked(&mm->page_table_lock); 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci pgtable = pmd_huge_pte(mm, pmdp); 6658c2ecf20Sopenharmony_ci lh = (struct list_head *) pgtable; 6668c2ecf20Sopenharmony_ci if (list_empty(lh)) 6678c2ecf20Sopenharmony_ci pmd_huge_pte(mm, pmdp) = NULL; 6688c2ecf20Sopenharmony_ci else { 6698c2ecf20Sopenharmony_ci pmd_huge_pte(mm, pmdp) = (pgtable_t) lh->next; 6708c2ecf20Sopenharmony_ci list_del(lh); 6718c2ecf20Sopenharmony_ci } 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci pte_val(pgtable[0]) = 0; 6748c2ecf20Sopenharmony_ci pte_val(pgtable[1]) = 0; 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci return pgtable; 6778c2ecf20Sopenharmony_ci} 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_civoid local_flush_pmd_tlb_range(struct vm_area_struct *vma, unsigned long start, 6808c2ecf20Sopenharmony_ci unsigned long end) 6818c2ecf20Sopenharmony_ci{ 6828c2ecf20Sopenharmony_ci unsigned int cpu; 6838c2ecf20Sopenharmony_ci unsigned long flags; 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci local_irq_save(flags); 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci cpu = smp_processor_id(); 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci if (likely(asid_mm(vma->vm_mm, cpu) != MM_CTXT_NO_ASID)) { 6908c2ecf20Sopenharmony_ci unsigned int asid = hw_pid(vma->vm_mm, cpu); 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci /* No need to loop here: this will always be for 1 Huge Page */ 6938c2ecf20Sopenharmony_ci tlb_entry_erase(start | _PAGE_HW_SZ | asid); 6948c2ecf20Sopenharmony_ci } 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci local_irq_restore(flags); 6978c2ecf20Sopenharmony_ci} 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci#endif 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci/* Read the Cache Build Configuration Registers, Decode them and save into 7028c2ecf20Sopenharmony_ci * the cpuinfo structure for later use. 7038c2ecf20Sopenharmony_ci * No Validation is done here, simply read/convert the BCRs 7048c2ecf20Sopenharmony_ci */ 7058c2ecf20Sopenharmony_civoid read_decode_mmu_bcr(void) 7068c2ecf20Sopenharmony_ci{ 7078c2ecf20Sopenharmony_ci struct cpuinfo_arc_mmu *mmu = &cpuinfo_arc700[smp_processor_id()].mmu; 7088c2ecf20Sopenharmony_ci unsigned int tmp; 7098c2ecf20Sopenharmony_ci struct bcr_mmu_1_2 { 7108c2ecf20Sopenharmony_ci#ifdef CONFIG_CPU_BIG_ENDIAN 7118c2ecf20Sopenharmony_ci unsigned int ver:8, ways:4, sets:4, u_itlb:8, u_dtlb:8; 7128c2ecf20Sopenharmony_ci#else 7138c2ecf20Sopenharmony_ci unsigned int u_dtlb:8, u_itlb:8, sets:4, ways:4, ver:8; 7148c2ecf20Sopenharmony_ci#endif 7158c2ecf20Sopenharmony_ci } *mmu2; 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci struct bcr_mmu_3 { 7188c2ecf20Sopenharmony_ci#ifdef CONFIG_CPU_BIG_ENDIAN 7198c2ecf20Sopenharmony_ci unsigned int ver:8, ways:4, sets:4, res:3, sasid:1, pg_sz:4, 7208c2ecf20Sopenharmony_ci u_itlb:4, u_dtlb:4; 7218c2ecf20Sopenharmony_ci#else 7228c2ecf20Sopenharmony_ci unsigned int u_dtlb:4, u_itlb:4, pg_sz:4, sasid:1, res:3, sets:4, 7238c2ecf20Sopenharmony_ci ways:4, ver:8; 7248c2ecf20Sopenharmony_ci#endif 7258c2ecf20Sopenharmony_ci } *mmu3; 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci struct bcr_mmu_4 { 7288c2ecf20Sopenharmony_ci#ifdef CONFIG_CPU_BIG_ENDIAN 7298c2ecf20Sopenharmony_ci unsigned int ver:8, sasid:1, sz1:4, sz0:4, res:2, pae:1, 7308c2ecf20Sopenharmony_ci n_ways:2, n_entry:2, n_super:2, u_itlb:3, u_dtlb:3; 7318c2ecf20Sopenharmony_ci#else 7328c2ecf20Sopenharmony_ci /* DTLB ITLB JES JE JA */ 7338c2ecf20Sopenharmony_ci unsigned int u_dtlb:3, u_itlb:3, n_super:2, n_entry:2, n_ways:2, 7348c2ecf20Sopenharmony_ci pae:1, res:2, sz0:4, sz1:4, sasid:1, ver:8; 7358c2ecf20Sopenharmony_ci#endif 7368c2ecf20Sopenharmony_ci } *mmu4; 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci tmp = read_aux_reg(ARC_REG_MMU_BCR); 7398c2ecf20Sopenharmony_ci mmu->ver = (tmp >> 24); 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci if (is_isa_arcompact()) { 7428c2ecf20Sopenharmony_ci if (mmu->ver <= 2) { 7438c2ecf20Sopenharmony_ci mmu2 = (struct bcr_mmu_1_2 *)&tmp; 7448c2ecf20Sopenharmony_ci mmu->pg_sz_k = TO_KB(0x2000); 7458c2ecf20Sopenharmony_ci mmu->sets = 1 << mmu2->sets; 7468c2ecf20Sopenharmony_ci mmu->ways = 1 << mmu2->ways; 7478c2ecf20Sopenharmony_ci mmu->u_dtlb = mmu2->u_dtlb; 7488c2ecf20Sopenharmony_ci mmu->u_itlb = mmu2->u_itlb; 7498c2ecf20Sopenharmony_ci } else { 7508c2ecf20Sopenharmony_ci mmu3 = (struct bcr_mmu_3 *)&tmp; 7518c2ecf20Sopenharmony_ci mmu->pg_sz_k = 1 << (mmu3->pg_sz - 1); 7528c2ecf20Sopenharmony_ci mmu->sets = 1 << mmu3->sets; 7538c2ecf20Sopenharmony_ci mmu->ways = 1 << mmu3->ways; 7548c2ecf20Sopenharmony_ci mmu->u_dtlb = mmu3->u_dtlb; 7558c2ecf20Sopenharmony_ci mmu->u_itlb = mmu3->u_itlb; 7568c2ecf20Sopenharmony_ci mmu->sasid = mmu3->sasid; 7578c2ecf20Sopenharmony_ci } 7588c2ecf20Sopenharmony_ci } else { 7598c2ecf20Sopenharmony_ci mmu4 = (struct bcr_mmu_4 *)&tmp; 7608c2ecf20Sopenharmony_ci mmu->pg_sz_k = 1 << (mmu4->sz0 - 1); 7618c2ecf20Sopenharmony_ci mmu->s_pg_sz_m = 1 << (mmu4->sz1 - 11); 7628c2ecf20Sopenharmony_ci mmu->sets = 64 << mmu4->n_entry; 7638c2ecf20Sopenharmony_ci mmu->ways = mmu4->n_ways * 2; 7648c2ecf20Sopenharmony_ci mmu->u_dtlb = mmu4->u_dtlb * 4; 7658c2ecf20Sopenharmony_ci mmu->u_itlb = mmu4->u_itlb * 4; 7668c2ecf20Sopenharmony_ci mmu->sasid = mmu4->sasid; 7678c2ecf20Sopenharmony_ci pae_exists = mmu->pae = mmu4->pae; 7688c2ecf20Sopenharmony_ci } 7698c2ecf20Sopenharmony_ci} 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_cichar *arc_mmu_mumbojumbo(int cpu_id, char *buf, int len) 7728c2ecf20Sopenharmony_ci{ 7738c2ecf20Sopenharmony_ci int n = 0; 7748c2ecf20Sopenharmony_ci struct cpuinfo_arc_mmu *p_mmu = &cpuinfo_arc700[cpu_id].mmu; 7758c2ecf20Sopenharmony_ci char super_pg[64] = ""; 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci if (p_mmu->s_pg_sz_m) 7788c2ecf20Sopenharmony_ci scnprintf(super_pg, 64, "%dM Super Page %s", 7798c2ecf20Sopenharmony_ci p_mmu->s_pg_sz_m, 7808c2ecf20Sopenharmony_ci IS_USED_CFG(CONFIG_TRANSPARENT_HUGEPAGE)); 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci n += scnprintf(buf + n, len - n, 7838c2ecf20Sopenharmony_ci "MMU [v%x]\t: %dk PAGE, %sJTLB %d (%dx%d), uDTLB %d, uITLB %d%s%s\n", 7848c2ecf20Sopenharmony_ci p_mmu->ver, p_mmu->pg_sz_k, super_pg, 7858c2ecf20Sopenharmony_ci p_mmu->sets * p_mmu->ways, p_mmu->sets, p_mmu->ways, 7868c2ecf20Sopenharmony_ci p_mmu->u_dtlb, p_mmu->u_itlb, 7878c2ecf20Sopenharmony_ci IS_AVAIL2(p_mmu->pae, ", PAE40 ", CONFIG_ARC_HAS_PAE40)); 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci return buf; 7908c2ecf20Sopenharmony_ci} 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ciint pae40_exist_but_not_enab(void) 7938c2ecf20Sopenharmony_ci{ 7948c2ecf20Sopenharmony_ci return pae_exists && !is_pae40_enabled(); 7958c2ecf20Sopenharmony_ci} 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_civoid arc_mmu_init(void) 7988c2ecf20Sopenharmony_ci{ 7998c2ecf20Sopenharmony_ci struct cpuinfo_arc_mmu *mmu = &cpuinfo_arc700[smp_processor_id()].mmu; 8008c2ecf20Sopenharmony_ci char str[256]; 8018c2ecf20Sopenharmony_ci int compat = 0; 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci pr_info("%s", arc_mmu_mumbojumbo(0, str, sizeof(str))); 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci /* 8068c2ecf20Sopenharmony_ci * Can't be done in processor.h due to header include dependencies 8078c2ecf20Sopenharmony_ci */ 8088c2ecf20Sopenharmony_ci BUILD_BUG_ON(!IS_ALIGNED((CONFIG_ARC_KVADDR_SIZE << 20), PMD_SIZE)); 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci /* 8118c2ecf20Sopenharmony_ci * stack top size sanity check, 8128c2ecf20Sopenharmony_ci * Can't be done in processor.h due to header include dependencies 8138c2ecf20Sopenharmony_ci */ 8148c2ecf20Sopenharmony_ci BUILD_BUG_ON(!IS_ALIGNED(STACK_TOP, PMD_SIZE)); 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci /* 8178c2ecf20Sopenharmony_ci * Ensure that MMU features assumed by kernel exist in hardware. 8188c2ecf20Sopenharmony_ci * For older ARC700 cpus, it has to be exact match, since the MMU 8198c2ecf20Sopenharmony_ci * revisions were not backwards compatible (MMUv3 TLB layout changed 8208c2ecf20Sopenharmony_ci * so even if kernel for v2 didn't use any new cmds of v3, it would 8218c2ecf20Sopenharmony_ci * still not work. 8228c2ecf20Sopenharmony_ci * For HS cpus, MMUv4 was baseline and v5 is backwards compatible 8238c2ecf20Sopenharmony_ci * (will run older software). 8248c2ecf20Sopenharmony_ci */ 8258c2ecf20Sopenharmony_ci if (is_isa_arcompact() && mmu->ver == CONFIG_ARC_MMU_VER) 8268c2ecf20Sopenharmony_ci compat = 1; 8278c2ecf20Sopenharmony_ci else if (is_isa_arcv2() && mmu->ver >= CONFIG_ARC_MMU_VER) 8288c2ecf20Sopenharmony_ci compat = 1; 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci if (!compat) { 8318c2ecf20Sopenharmony_ci panic("MMU ver %d doesn't match kernel built for %d...\n", 8328c2ecf20Sopenharmony_ci mmu->ver, CONFIG_ARC_MMU_VER); 8338c2ecf20Sopenharmony_ci } 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci if (mmu->pg_sz_k != TO_KB(PAGE_SIZE)) 8368c2ecf20Sopenharmony_ci panic("MMU pg size != PAGE_SIZE (%luk)\n", TO_KB(PAGE_SIZE)); 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) && 8398c2ecf20Sopenharmony_ci mmu->s_pg_sz_m != TO_MB(HPAGE_PMD_SIZE)) 8408c2ecf20Sopenharmony_ci panic("MMU Super pg size != Linux HPAGE_PMD_SIZE (%luM)\n", 8418c2ecf20Sopenharmony_ci (unsigned long)TO_MB(HPAGE_PMD_SIZE)); 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_ARC_HAS_PAE40) && !mmu->pae) 8448c2ecf20Sopenharmony_ci panic("Hardware doesn't support PAE40\n"); 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci /* Enable the MMU */ 8478c2ecf20Sopenharmony_ci write_aux_reg(ARC_REG_PID, MMU_ENABLE); 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci /* In smp we use this reg for interrupt 1 scratch */ 8508c2ecf20Sopenharmony_ci#ifdef ARC_USE_SCRATCH_REG 8518c2ecf20Sopenharmony_ci /* swapper_pg_dir is the pgd for the kernel, used by vmalloc */ 8528c2ecf20Sopenharmony_ci write_aux_reg(ARC_REG_SCRATCH_DATA0, swapper_pg_dir); 8538c2ecf20Sopenharmony_ci#endif 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci if (pae40_exist_but_not_enab()) 8568c2ecf20Sopenharmony_ci write_aux_reg(ARC_REG_TLBPD1HI, 0); 8578c2ecf20Sopenharmony_ci} 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci/* 8608c2ecf20Sopenharmony_ci * TLB Programmer's Model uses Linear Indexes: 0 to {255, 511} for 128 x {2,4} 8618c2ecf20Sopenharmony_ci * The mapping is Column-first. 8628c2ecf20Sopenharmony_ci * --------------------- ----------- 8638c2ecf20Sopenharmony_ci * |way0|way1|way2|way3| |way0|way1| 8648c2ecf20Sopenharmony_ci * --------------------- ----------- 8658c2ecf20Sopenharmony_ci * [set0] | 0 | 1 | 2 | 3 | | 0 | 1 | 8668c2ecf20Sopenharmony_ci * [set1] | 4 | 5 | 6 | 7 | | 2 | 3 | 8678c2ecf20Sopenharmony_ci * ~ ~ ~ ~ 8688c2ecf20Sopenharmony_ci * [set127] | 508| 509| 510| 511| | 254| 255| 8698c2ecf20Sopenharmony_ci * --------------------- ----------- 8708c2ecf20Sopenharmony_ci * For normal operations we don't(must not) care how above works since 8718c2ecf20Sopenharmony_ci * MMU cmd getIndex(vaddr) abstracts that out. 8728c2ecf20Sopenharmony_ci * However for walking WAYS of a SET, we need to know this 8738c2ecf20Sopenharmony_ci */ 8748c2ecf20Sopenharmony_ci#define SET_WAY_TO_IDX(mmu, set, way) ((set) * mmu->ways + (way)) 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_ci/* Handling of Duplicate PD (TLB entry) in MMU. 8778c2ecf20Sopenharmony_ci * -Could be due to buggy customer tapeouts or obscure kernel bugs 8788c2ecf20Sopenharmony_ci * -MMU complaints not at the time of duplicate PD installation, but at the 8798c2ecf20Sopenharmony_ci * time of lookup matching multiple ways. 8808c2ecf20Sopenharmony_ci * -Ideally these should never happen - but if they do - workaround by deleting 8818c2ecf20Sopenharmony_ci * the duplicate one. 8828c2ecf20Sopenharmony_ci * -Knob to be verbose abt it.(TODO: hook them up to debugfs) 8838c2ecf20Sopenharmony_ci */ 8848c2ecf20Sopenharmony_civolatile int dup_pd_silent; /* Be silent abt it or complain (default) */ 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_civoid do_tlb_overlap_fault(unsigned long cause, unsigned long address, 8878c2ecf20Sopenharmony_ci struct pt_regs *regs) 8888c2ecf20Sopenharmony_ci{ 8898c2ecf20Sopenharmony_ci struct cpuinfo_arc_mmu *mmu = &cpuinfo_arc700[smp_processor_id()].mmu; 8908c2ecf20Sopenharmony_ci unsigned long flags; 8918c2ecf20Sopenharmony_ci int set, n_ways = mmu->ways; 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci n_ways = min(n_ways, 4); 8948c2ecf20Sopenharmony_ci BUG_ON(mmu->ways > 4); 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_ci local_irq_save(flags); 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_ci /* loop thru all sets of TLB */ 8998c2ecf20Sopenharmony_ci for (set = 0; set < mmu->sets; set++) { 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci int is_valid, way; 9028c2ecf20Sopenharmony_ci unsigned int pd0[4]; 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_ci /* read out all the ways of current set */ 9058c2ecf20Sopenharmony_ci for (way = 0, is_valid = 0; way < n_ways; way++) { 9068c2ecf20Sopenharmony_ci write_aux_reg(ARC_REG_TLBINDEX, 9078c2ecf20Sopenharmony_ci SET_WAY_TO_IDX(mmu, set, way)); 9088c2ecf20Sopenharmony_ci write_aux_reg(ARC_REG_TLBCOMMAND, TLBRead); 9098c2ecf20Sopenharmony_ci pd0[way] = read_aux_reg(ARC_REG_TLBPD0); 9108c2ecf20Sopenharmony_ci is_valid |= pd0[way] & _PAGE_PRESENT; 9118c2ecf20Sopenharmony_ci pd0[way] &= PAGE_MASK; 9128c2ecf20Sopenharmony_ci } 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci /* If all the WAYS in SET are empty, skip to next SET */ 9158c2ecf20Sopenharmony_ci if (!is_valid) 9168c2ecf20Sopenharmony_ci continue; 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ci /* Scan the set for duplicate ways: needs a nested loop */ 9198c2ecf20Sopenharmony_ci for (way = 0; way < n_ways - 1; way++) { 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ci int n; 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_ci if (!pd0[way]) 9248c2ecf20Sopenharmony_ci continue; 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ci for (n = way + 1; n < n_ways; n++) { 9278c2ecf20Sopenharmony_ci if (pd0[way] != pd0[n]) 9288c2ecf20Sopenharmony_ci continue; 9298c2ecf20Sopenharmony_ci 9308c2ecf20Sopenharmony_ci if (!dup_pd_silent) 9318c2ecf20Sopenharmony_ci pr_info("Dup TLB PD0 %08x @ set %d ways %d,%d\n", 9328c2ecf20Sopenharmony_ci pd0[way], set, way, n); 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ci /* 9358c2ecf20Sopenharmony_ci * clear entry @way and not @n. 9368c2ecf20Sopenharmony_ci * This is critical to our optimised loop 9378c2ecf20Sopenharmony_ci */ 9388c2ecf20Sopenharmony_ci pd0[way] = 0; 9398c2ecf20Sopenharmony_ci write_aux_reg(ARC_REG_TLBINDEX, 9408c2ecf20Sopenharmony_ci SET_WAY_TO_IDX(mmu, set, way)); 9418c2ecf20Sopenharmony_ci __tlb_entry_erase(); 9428c2ecf20Sopenharmony_ci } 9438c2ecf20Sopenharmony_ci } 9448c2ecf20Sopenharmony_ci } 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci local_irq_restore(flags); 9478c2ecf20Sopenharmony_ci} 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_ci/*********************************************************************** 9508c2ecf20Sopenharmony_ci * Diagnostic Routines 9518c2ecf20Sopenharmony_ci * -Called from Low Level TLB Handlers if things don;t look good 9528c2ecf20Sopenharmony_ci **********************************************************************/ 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_ci#ifdef CONFIG_ARC_DBG_TLB_PARANOIA 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci/* 9578c2ecf20Sopenharmony_ci * Low Level ASM TLB handler calls this if it finds that HW and SW ASIDS 9588c2ecf20Sopenharmony_ci * don't match 9598c2ecf20Sopenharmony_ci */ 9608c2ecf20Sopenharmony_civoid print_asid_mismatch(int mm_asid, int mmu_asid, int is_fast_path) 9618c2ecf20Sopenharmony_ci{ 9628c2ecf20Sopenharmony_ci pr_emerg("ASID Mismatch in %s Path Handler: sw-pid=0x%x hw-pid=0x%x\n", 9638c2ecf20Sopenharmony_ci is_fast_path ? "Fast" : "Slow", mm_asid, mmu_asid); 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci __asm__ __volatile__("flag 1"); 9668c2ecf20Sopenharmony_ci} 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_civoid tlb_paranoid_check(unsigned int mm_asid, unsigned long addr) 9698c2ecf20Sopenharmony_ci{ 9708c2ecf20Sopenharmony_ci unsigned int mmu_asid; 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_ci mmu_asid = read_aux_reg(ARC_REG_PID) & 0xff; 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_ci /* 9758c2ecf20Sopenharmony_ci * At the time of a TLB miss/installation 9768c2ecf20Sopenharmony_ci * - HW version needs to match SW version 9778c2ecf20Sopenharmony_ci * - SW needs to have a valid ASID 9788c2ecf20Sopenharmony_ci */ 9798c2ecf20Sopenharmony_ci if (addr < 0x70000000 && 9808c2ecf20Sopenharmony_ci ((mm_asid == MM_CTXT_NO_ASID) || 9818c2ecf20Sopenharmony_ci (mmu_asid != (mm_asid & MM_CTXT_ASID_MASK)))) 9828c2ecf20Sopenharmony_ci print_asid_mismatch(mm_asid, mmu_asid, 0); 9838c2ecf20Sopenharmony_ci} 9848c2ecf20Sopenharmony_ci#endif 985