18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 38c2ecf20Sopenharmony_ci * License. See the file "COPYING" in the main directory of this archive 48c2ecf20Sopenharmony_ci * for more details. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Synthesize TLB refill handlers at runtime. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Copyright (C) 2004, 2005, 2006, 2008 Thiemo Seufer 98c2ecf20Sopenharmony_ci * Copyright (C) 2005, 2007, 2008, 2009 Maciej W. Rozycki 108c2ecf20Sopenharmony_ci * Copyright (C) 2006 Ralf Baechle (ralf@linux-mips.org) 118c2ecf20Sopenharmony_ci * Copyright (C) 2008, 2009 Cavium Networks, Inc. 128c2ecf20Sopenharmony_ci * Copyright (C) 2011 MIPS Technologies, Inc. 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * ... and the days got worse and worse and now you see 158c2ecf20Sopenharmony_ci * I've gone completely out of my mind. 168c2ecf20Sopenharmony_ci * 178c2ecf20Sopenharmony_ci * They're coming to take me a away haha 188c2ecf20Sopenharmony_ci * they're coming to take me a away hoho hihi haha 198c2ecf20Sopenharmony_ci * to the funny farm where code is beautiful all the time ... 208c2ecf20Sopenharmony_ci * 218c2ecf20Sopenharmony_ci * (Condolences to Napoleon XIV) 228c2ecf20Sopenharmony_ci */ 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#include <linux/bug.h> 258c2ecf20Sopenharmony_ci#include <linux/export.h> 268c2ecf20Sopenharmony_ci#include <linux/kernel.h> 278c2ecf20Sopenharmony_ci#include <linux/types.h> 288c2ecf20Sopenharmony_ci#include <linux/smp.h> 298c2ecf20Sopenharmony_ci#include <linux/string.h> 308c2ecf20Sopenharmony_ci#include <linux/cache.h> 318c2ecf20Sopenharmony_ci#include <linux/pgtable.h> 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#include <asm/cacheflush.h> 348c2ecf20Sopenharmony_ci#include <asm/cpu-type.h> 358c2ecf20Sopenharmony_ci#include <asm/mmu_context.h> 368c2ecf20Sopenharmony_ci#include <asm/war.h> 378c2ecf20Sopenharmony_ci#include <asm/uasm.h> 388c2ecf20Sopenharmony_ci#include <asm/setup.h> 398c2ecf20Sopenharmony_ci#include <asm/tlbex.h> 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic int mips_xpa_disabled; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic int __init xpa_disable(char *s) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci mips_xpa_disabled = 1; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci return 1; 488c2ecf20Sopenharmony_ci} 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci__setup("noxpa", xpa_disable); 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci/* 538c2ecf20Sopenharmony_ci * TLB load/store/modify handlers. 548c2ecf20Sopenharmony_ci * 558c2ecf20Sopenharmony_ci * Only the fastpath gets synthesized at runtime, the slowpath for 568c2ecf20Sopenharmony_ci * do_page_fault remains normal asm. 578c2ecf20Sopenharmony_ci */ 588c2ecf20Sopenharmony_ciextern void tlb_do_page_fault_0(void); 598c2ecf20Sopenharmony_ciextern void tlb_do_page_fault_1(void); 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistruct work_registers { 628c2ecf20Sopenharmony_ci int r1; 638c2ecf20Sopenharmony_ci int r2; 648c2ecf20Sopenharmony_ci int r3; 658c2ecf20Sopenharmony_ci}; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistruct tlb_reg_save { 688c2ecf20Sopenharmony_ci unsigned long a; 698c2ecf20Sopenharmony_ci unsigned long b; 708c2ecf20Sopenharmony_ci} ____cacheline_aligned_in_smp; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistatic struct tlb_reg_save handler_reg_save[NR_CPUS]; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistatic inline int r45k_bvahwbug(void) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci /* XXX: We should probe for the presence of this bug, but we don't. */ 778c2ecf20Sopenharmony_ci return 0; 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic inline int r4k_250MHZhwbug(void) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci /* XXX: We should probe for the presence of this bug, but we don't. */ 838c2ecf20Sopenharmony_ci return 0; 848c2ecf20Sopenharmony_ci} 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ciextern int sb1250_m3_workaround_needed(void); 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistatic inline int __maybe_unused bcm1250_m3_war(void) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_SB1_PASS_2_WORKAROUNDS)) 918c2ecf20Sopenharmony_ci return sb1250_m3_workaround_needed(); 928c2ecf20Sopenharmony_ci return 0; 938c2ecf20Sopenharmony_ci} 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_cistatic inline int __maybe_unused r10000_llsc_war(void) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci return IS_ENABLED(CONFIG_WAR_R10000_LLSC); 988c2ecf20Sopenharmony_ci} 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistatic int use_bbit_insns(void) 1018c2ecf20Sopenharmony_ci{ 1028c2ecf20Sopenharmony_ci switch (current_cpu_type()) { 1038c2ecf20Sopenharmony_ci case CPU_CAVIUM_OCTEON: 1048c2ecf20Sopenharmony_ci case CPU_CAVIUM_OCTEON_PLUS: 1058c2ecf20Sopenharmony_ci case CPU_CAVIUM_OCTEON2: 1068c2ecf20Sopenharmony_ci case CPU_CAVIUM_OCTEON3: 1078c2ecf20Sopenharmony_ci return 1; 1088c2ecf20Sopenharmony_ci default: 1098c2ecf20Sopenharmony_ci return 0; 1108c2ecf20Sopenharmony_ci } 1118c2ecf20Sopenharmony_ci} 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_cistatic int use_lwx_insns(void) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci switch (current_cpu_type()) { 1168c2ecf20Sopenharmony_ci case CPU_CAVIUM_OCTEON2: 1178c2ecf20Sopenharmony_ci case CPU_CAVIUM_OCTEON3: 1188c2ecf20Sopenharmony_ci return 1; 1198c2ecf20Sopenharmony_ci default: 1208c2ecf20Sopenharmony_ci return 0; 1218c2ecf20Sopenharmony_ci } 1228c2ecf20Sopenharmony_ci} 1238c2ecf20Sopenharmony_ci#if defined(CONFIG_CAVIUM_OCTEON_CVMSEG_SIZE) && \ 1248c2ecf20Sopenharmony_ci CONFIG_CAVIUM_OCTEON_CVMSEG_SIZE > 0 1258c2ecf20Sopenharmony_cistatic bool scratchpad_available(void) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci return true; 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_cistatic int scratchpad_offset(int i) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci /* 1328c2ecf20Sopenharmony_ci * CVMSEG starts at address -32768 and extends for 1338c2ecf20Sopenharmony_ci * CAVIUM_OCTEON_CVMSEG_SIZE 128 byte cache lines. 1348c2ecf20Sopenharmony_ci */ 1358c2ecf20Sopenharmony_ci i += 1; /* Kernel use starts at the top and works down. */ 1368c2ecf20Sopenharmony_ci return CONFIG_CAVIUM_OCTEON_CVMSEG_SIZE * 128 - (8 * i) - 32768; 1378c2ecf20Sopenharmony_ci} 1388c2ecf20Sopenharmony_ci#else 1398c2ecf20Sopenharmony_cistatic bool scratchpad_available(void) 1408c2ecf20Sopenharmony_ci{ 1418c2ecf20Sopenharmony_ci return false; 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_cistatic int scratchpad_offset(int i) 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci BUG(); 1468c2ecf20Sopenharmony_ci /* Really unreachable, but evidently some GCC want this. */ 1478c2ecf20Sopenharmony_ci return 0; 1488c2ecf20Sopenharmony_ci} 1498c2ecf20Sopenharmony_ci#endif 1508c2ecf20Sopenharmony_ci/* 1518c2ecf20Sopenharmony_ci * Found by experiment: At least some revisions of the 4kc throw under 1528c2ecf20Sopenharmony_ci * some circumstances a machine check exception, triggered by invalid 1538c2ecf20Sopenharmony_ci * values in the index register. Delaying the tlbp instruction until 1548c2ecf20Sopenharmony_ci * after the next branch, plus adding an additional nop in front of 1558c2ecf20Sopenharmony_ci * tlbwi/tlbwr avoids the invalid index register values. Nobody knows 1568c2ecf20Sopenharmony_ci * why; it's not an issue caused by the core RTL. 1578c2ecf20Sopenharmony_ci * 1588c2ecf20Sopenharmony_ci */ 1598c2ecf20Sopenharmony_cistatic int m4kc_tlbp_war(void) 1608c2ecf20Sopenharmony_ci{ 1618c2ecf20Sopenharmony_ci return current_cpu_type() == CPU_4KC; 1628c2ecf20Sopenharmony_ci} 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci/* Handle labels (which must be positive integers). */ 1658c2ecf20Sopenharmony_cienum label_id { 1668c2ecf20Sopenharmony_ci label_second_part = 1, 1678c2ecf20Sopenharmony_ci label_leave, 1688c2ecf20Sopenharmony_ci label_vmalloc, 1698c2ecf20Sopenharmony_ci label_vmalloc_done, 1708c2ecf20Sopenharmony_ci label_tlbw_hazard_0, 1718c2ecf20Sopenharmony_ci label_split = label_tlbw_hazard_0 + 8, 1728c2ecf20Sopenharmony_ci label_tlbl_goaround1, 1738c2ecf20Sopenharmony_ci label_tlbl_goaround2, 1748c2ecf20Sopenharmony_ci label_nopage_tlbl, 1758c2ecf20Sopenharmony_ci label_nopage_tlbs, 1768c2ecf20Sopenharmony_ci label_nopage_tlbm, 1778c2ecf20Sopenharmony_ci label_smp_pgtable_change, 1788c2ecf20Sopenharmony_ci label_r3000_write_probe_fail, 1798c2ecf20Sopenharmony_ci label_large_segbits_fault, 1808c2ecf20Sopenharmony_ci#ifdef CONFIG_MIPS_HUGE_TLB_SUPPORT 1818c2ecf20Sopenharmony_ci label_tlb_huge_update, 1828c2ecf20Sopenharmony_ci#endif 1838c2ecf20Sopenharmony_ci}; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ciUASM_L_LA(_second_part) 1868c2ecf20Sopenharmony_ciUASM_L_LA(_leave) 1878c2ecf20Sopenharmony_ciUASM_L_LA(_vmalloc) 1888c2ecf20Sopenharmony_ciUASM_L_LA(_vmalloc_done) 1898c2ecf20Sopenharmony_ci/* _tlbw_hazard_x is handled differently. */ 1908c2ecf20Sopenharmony_ciUASM_L_LA(_split) 1918c2ecf20Sopenharmony_ciUASM_L_LA(_tlbl_goaround1) 1928c2ecf20Sopenharmony_ciUASM_L_LA(_tlbl_goaround2) 1938c2ecf20Sopenharmony_ciUASM_L_LA(_nopage_tlbl) 1948c2ecf20Sopenharmony_ciUASM_L_LA(_nopage_tlbs) 1958c2ecf20Sopenharmony_ciUASM_L_LA(_nopage_tlbm) 1968c2ecf20Sopenharmony_ciUASM_L_LA(_smp_pgtable_change) 1978c2ecf20Sopenharmony_ciUASM_L_LA(_r3000_write_probe_fail) 1988c2ecf20Sopenharmony_ciUASM_L_LA(_large_segbits_fault) 1998c2ecf20Sopenharmony_ci#ifdef CONFIG_MIPS_HUGE_TLB_SUPPORT 2008c2ecf20Sopenharmony_ciUASM_L_LA(_tlb_huge_update) 2018c2ecf20Sopenharmony_ci#endif 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_cistatic int hazard_instance; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_cistatic void uasm_bgezl_hazard(u32 **p, struct uasm_reloc **r, int instance) 2068c2ecf20Sopenharmony_ci{ 2078c2ecf20Sopenharmony_ci switch (instance) { 2088c2ecf20Sopenharmony_ci case 0 ... 7: 2098c2ecf20Sopenharmony_ci uasm_il_bgezl(p, r, 0, label_tlbw_hazard_0 + instance); 2108c2ecf20Sopenharmony_ci return; 2118c2ecf20Sopenharmony_ci default: 2128c2ecf20Sopenharmony_ci BUG(); 2138c2ecf20Sopenharmony_ci } 2148c2ecf20Sopenharmony_ci} 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_cistatic void uasm_bgezl_label(struct uasm_label **l, u32 **p, int instance) 2178c2ecf20Sopenharmony_ci{ 2188c2ecf20Sopenharmony_ci switch (instance) { 2198c2ecf20Sopenharmony_ci case 0 ... 7: 2208c2ecf20Sopenharmony_ci uasm_build_label(l, *p, label_tlbw_hazard_0 + instance); 2218c2ecf20Sopenharmony_ci break; 2228c2ecf20Sopenharmony_ci default: 2238c2ecf20Sopenharmony_ci BUG(); 2248c2ecf20Sopenharmony_ci } 2258c2ecf20Sopenharmony_ci} 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci/* 2288c2ecf20Sopenharmony_ci * pgtable bits are assigned dynamically depending on processor feature 2298c2ecf20Sopenharmony_ci * and statically based on kernel configuration. This spits out the actual 2308c2ecf20Sopenharmony_ci * values the kernel is using. Required to make sense from disassembled 2318c2ecf20Sopenharmony_ci * TLB exception handlers. 2328c2ecf20Sopenharmony_ci */ 2338c2ecf20Sopenharmony_cistatic void output_pgtable_bits_defines(void) 2348c2ecf20Sopenharmony_ci{ 2358c2ecf20Sopenharmony_ci#define pr_define(fmt, ...) \ 2368c2ecf20Sopenharmony_ci pr_debug("#define " fmt, ##__VA_ARGS__) 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci pr_debug("#include <asm/asm.h>\n"); 2398c2ecf20Sopenharmony_ci pr_debug("#include <asm/regdef.h>\n"); 2408c2ecf20Sopenharmony_ci pr_debug("\n"); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci pr_define("_PAGE_PRESENT_SHIFT %d\n", _PAGE_PRESENT_SHIFT); 2438c2ecf20Sopenharmony_ci pr_define("_PAGE_NO_READ_SHIFT %d\n", _PAGE_NO_READ_SHIFT); 2448c2ecf20Sopenharmony_ci pr_define("_PAGE_WRITE_SHIFT %d\n", _PAGE_WRITE_SHIFT); 2458c2ecf20Sopenharmony_ci pr_define("_PAGE_ACCESSED_SHIFT %d\n", _PAGE_ACCESSED_SHIFT); 2468c2ecf20Sopenharmony_ci pr_define("_PAGE_MODIFIED_SHIFT %d\n", _PAGE_MODIFIED_SHIFT); 2478c2ecf20Sopenharmony_ci#ifdef CONFIG_MIPS_HUGE_TLB_SUPPORT 2488c2ecf20Sopenharmony_ci pr_define("_PAGE_HUGE_SHIFT %d\n", _PAGE_HUGE_SHIFT); 2498c2ecf20Sopenharmony_ci#endif 2508c2ecf20Sopenharmony_ci#ifdef _PAGE_NO_EXEC_SHIFT 2518c2ecf20Sopenharmony_ci if (cpu_has_rixi) 2528c2ecf20Sopenharmony_ci pr_define("_PAGE_NO_EXEC_SHIFT %d\n", _PAGE_NO_EXEC_SHIFT); 2538c2ecf20Sopenharmony_ci#endif 2548c2ecf20Sopenharmony_ci pr_define("_PAGE_GLOBAL_SHIFT %d\n", _PAGE_GLOBAL_SHIFT); 2558c2ecf20Sopenharmony_ci pr_define("_PAGE_VALID_SHIFT %d\n", _PAGE_VALID_SHIFT); 2568c2ecf20Sopenharmony_ci pr_define("_PAGE_DIRTY_SHIFT %d\n", _PAGE_DIRTY_SHIFT); 2578c2ecf20Sopenharmony_ci pr_define("_PFN_SHIFT %d\n", _PFN_SHIFT); 2588c2ecf20Sopenharmony_ci pr_debug("\n"); 2598c2ecf20Sopenharmony_ci} 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_cistatic inline void dump_handler(const char *symbol, const void *start, const void *end) 2628c2ecf20Sopenharmony_ci{ 2638c2ecf20Sopenharmony_ci unsigned int count = (end - start) / sizeof(u32); 2648c2ecf20Sopenharmony_ci const u32 *handler = start; 2658c2ecf20Sopenharmony_ci int i; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci pr_debug("LEAF(%s)\n", symbol); 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci pr_debug("\t.set push\n"); 2708c2ecf20Sopenharmony_ci pr_debug("\t.set noreorder\n"); 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci for (i = 0; i < count; i++) 2738c2ecf20Sopenharmony_ci pr_debug("\t.word\t0x%08x\t\t# %p\n", handler[i], &handler[i]); 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci pr_debug("\t.set\tpop\n"); 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci pr_debug("\tEND(%s)\n", symbol); 2788c2ecf20Sopenharmony_ci} 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci/* The only general purpose registers allowed in TLB handlers. */ 2818c2ecf20Sopenharmony_ci#define K0 26 2828c2ecf20Sopenharmony_ci#define K1 27 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci/* Some CP0 registers */ 2858c2ecf20Sopenharmony_ci#define C0_INDEX 0, 0 2868c2ecf20Sopenharmony_ci#define C0_ENTRYLO0 2, 0 2878c2ecf20Sopenharmony_ci#define C0_TCBIND 2, 2 2888c2ecf20Sopenharmony_ci#define C0_ENTRYLO1 3, 0 2898c2ecf20Sopenharmony_ci#define C0_CONTEXT 4, 0 2908c2ecf20Sopenharmony_ci#define C0_PAGEMASK 5, 0 2918c2ecf20Sopenharmony_ci#define C0_PWBASE 5, 5 2928c2ecf20Sopenharmony_ci#define C0_PWFIELD 5, 6 2938c2ecf20Sopenharmony_ci#define C0_PWSIZE 5, 7 2948c2ecf20Sopenharmony_ci#define C0_PWCTL 6, 6 2958c2ecf20Sopenharmony_ci#define C0_BADVADDR 8, 0 2968c2ecf20Sopenharmony_ci#define C0_PGD 9, 7 2978c2ecf20Sopenharmony_ci#define C0_ENTRYHI 10, 0 2988c2ecf20Sopenharmony_ci#define C0_EPC 14, 0 2998c2ecf20Sopenharmony_ci#define C0_XCONTEXT 20, 0 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci#ifdef CONFIG_64BIT 3028c2ecf20Sopenharmony_ci# define GET_CONTEXT(buf, reg) UASM_i_MFC0(buf, reg, C0_XCONTEXT) 3038c2ecf20Sopenharmony_ci#else 3048c2ecf20Sopenharmony_ci# define GET_CONTEXT(buf, reg) UASM_i_MFC0(buf, reg, C0_CONTEXT) 3058c2ecf20Sopenharmony_ci#endif 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci/* The worst case length of the handler is around 18 instructions for 3088c2ecf20Sopenharmony_ci * R3000-style TLBs and up to 63 instructions for R4000-style TLBs. 3098c2ecf20Sopenharmony_ci * Maximum space available is 32 instructions for R3000 and 64 3108c2ecf20Sopenharmony_ci * instructions for R4000. 3118c2ecf20Sopenharmony_ci * 3128c2ecf20Sopenharmony_ci * We deliberately chose a buffer size of 128, so we won't scribble 3138c2ecf20Sopenharmony_ci * over anything important on overflow before we panic. 3148c2ecf20Sopenharmony_ci */ 3158c2ecf20Sopenharmony_cistatic u32 tlb_handler[128]; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci/* simply assume worst case size for labels and relocs */ 3188c2ecf20Sopenharmony_cistatic struct uasm_label labels[128]; 3198c2ecf20Sopenharmony_cistatic struct uasm_reloc relocs[128]; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_cistatic int check_for_high_segbits; 3228c2ecf20Sopenharmony_cistatic bool fill_includes_sw_bits; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_cistatic unsigned int kscratch_used_mask; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_cistatic inline int __maybe_unused c0_kscratch(void) 3278c2ecf20Sopenharmony_ci{ 3288c2ecf20Sopenharmony_ci switch (current_cpu_type()) { 3298c2ecf20Sopenharmony_ci case CPU_XLP: 3308c2ecf20Sopenharmony_ci case CPU_XLR: 3318c2ecf20Sopenharmony_ci return 22; 3328c2ecf20Sopenharmony_ci default: 3338c2ecf20Sopenharmony_ci return 31; 3348c2ecf20Sopenharmony_ci } 3358c2ecf20Sopenharmony_ci} 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_cistatic int allocate_kscratch(void) 3388c2ecf20Sopenharmony_ci{ 3398c2ecf20Sopenharmony_ci int r; 3408c2ecf20Sopenharmony_ci unsigned int a = cpu_data[0].kscratch_mask & ~kscratch_used_mask; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci r = ffs(a); 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci if (r == 0) 3458c2ecf20Sopenharmony_ci return -1; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci r--; /* make it zero based */ 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci kscratch_used_mask |= (1 << r); 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci return r; 3528c2ecf20Sopenharmony_ci} 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_cistatic int scratch_reg; 3558c2ecf20Sopenharmony_ciint pgd_reg; 3568c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(pgd_reg); 3578c2ecf20Sopenharmony_cienum vmalloc64_mode {not_refill, refill_scratch, refill_noscratch}; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_cistatic struct work_registers build_get_work_registers(u32 **p) 3608c2ecf20Sopenharmony_ci{ 3618c2ecf20Sopenharmony_ci struct work_registers r; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci if (scratch_reg >= 0) { 3648c2ecf20Sopenharmony_ci /* Save in CPU local C0_KScratch? */ 3658c2ecf20Sopenharmony_ci UASM_i_MTC0(p, 1, c0_kscratch(), scratch_reg); 3668c2ecf20Sopenharmony_ci r.r1 = K0; 3678c2ecf20Sopenharmony_ci r.r2 = K1; 3688c2ecf20Sopenharmony_ci r.r3 = 1; 3698c2ecf20Sopenharmony_ci return r; 3708c2ecf20Sopenharmony_ci } 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci if (num_possible_cpus() > 1) { 3738c2ecf20Sopenharmony_ci /* Get smp_processor_id */ 3748c2ecf20Sopenharmony_ci UASM_i_CPUID_MFC0(p, K0, SMP_CPUID_REG); 3758c2ecf20Sopenharmony_ci UASM_i_SRL_SAFE(p, K0, K0, SMP_CPUID_REGSHIFT); 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci /* handler_reg_save index in K0 */ 3788c2ecf20Sopenharmony_ci UASM_i_SLL(p, K0, K0, ilog2(sizeof(struct tlb_reg_save))); 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci UASM_i_LA(p, K1, (long)&handler_reg_save); 3818c2ecf20Sopenharmony_ci UASM_i_ADDU(p, K0, K0, K1); 3828c2ecf20Sopenharmony_ci } else { 3838c2ecf20Sopenharmony_ci UASM_i_LA(p, K0, (long)&handler_reg_save); 3848c2ecf20Sopenharmony_ci } 3858c2ecf20Sopenharmony_ci /* K0 now points to save area, save $1 and $2 */ 3868c2ecf20Sopenharmony_ci UASM_i_SW(p, 1, offsetof(struct tlb_reg_save, a), K0); 3878c2ecf20Sopenharmony_ci UASM_i_SW(p, 2, offsetof(struct tlb_reg_save, b), K0); 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci r.r1 = K1; 3908c2ecf20Sopenharmony_ci r.r2 = 1; 3918c2ecf20Sopenharmony_ci r.r3 = 2; 3928c2ecf20Sopenharmony_ci return r; 3938c2ecf20Sopenharmony_ci} 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_cistatic void build_restore_work_registers(u32 **p) 3968c2ecf20Sopenharmony_ci{ 3978c2ecf20Sopenharmony_ci if (scratch_reg >= 0) { 3988c2ecf20Sopenharmony_ci uasm_i_ehb(p); 3998c2ecf20Sopenharmony_ci UASM_i_MFC0(p, 1, c0_kscratch(), scratch_reg); 4008c2ecf20Sopenharmony_ci return; 4018c2ecf20Sopenharmony_ci } 4028c2ecf20Sopenharmony_ci /* K0 already points to save area, restore $1 and $2 */ 4038c2ecf20Sopenharmony_ci UASM_i_LW(p, 1, offsetof(struct tlb_reg_save, a), K0); 4048c2ecf20Sopenharmony_ci UASM_i_LW(p, 2, offsetof(struct tlb_reg_save, b), K0); 4058c2ecf20Sopenharmony_ci} 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci#ifndef CONFIG_MIPS_PGD_C0_CONTEXT 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci/* 4108c2ecf20Sopenharmony_ci * CONFIG_MIPS_PGD_C0_CONTEXT implies 64 bit and lack of pgd_current, 4118c2ecf20Sopenharmony_ci * we cannot do r3000 under these circumstances. 4128c2ecf20Sopenharmony_ci * 4138c2ecf20Sopenharmony_ci * The R3000 TLB handler is simple. 4148c2ecf20Sopenharmony_ci */ 4158c2ecf20Sopenharmony_cistatic void build_r3000_tlb_refill_handler(void) 4168c2ecf20Sopenharmony_ci{ 4178c2ecf20Sopenharmony_ci long pgdc = (long)pgd_current; 4188c2ecf20Sopenharmony_ci u32 *p; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci memset(tlb_handler, 0, sizeof(tlb_handler)); 4218c2ecf20Sopenharmony_ci p = tlb_handler; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci uasm_i_mfc0(&p, K0, C0_BADVADDR); 4248c2ecf20Sopenharmony_ci uasm_i_lui(&p, K1, uasm_rel_hi(pgdc)); /* cp0 delay */ 4258c2ecf20Sopenharmony_ci uasm_i_lw(&p, K1, uasm_rel_lo(pgdc), K1); 4268c2ecf20Sopenharmony_ci uasm_i_srl(&p, K0, K0, 22); /* load delay */ 4278c2ecf20Sopenharmony_ci uasm_i_sll(&p, K0, K0, 2); 4288c2ecf20Sopenharmony_ci uasm_i_addu(&p, K1, K1, K0); 4298c2ecf20Sopenharmony_ci uasm_i_mfc0(&p, K0, C0_CONTEXT); 4308c2ecf20Sopenharmony_ci uasm_i_lw(&p, K1, 0, K1); /* cp0 delay */ 4318c2ecf20Sopenharmony_ci uasm_i_andi(&p, K0, K0, 0xffc); /* load delay */ 4328c2ecf20Sopenharmony_ci uasm_i_addu(&p, K1, K1, K0); 4338c2ecf20Sopenharmony_ci uasm_i_lw(&p, K0, 0, K1); 4348c2ecf20Sopenharmony_ci uasm_i_nop(&p); /* load delay */ 4358c2ecf20Sopenharmony_ci uasm_i_mtc0(&p, K0, C0_ENTRYLO0); 4368c2ecf20Sopenharmony_ci uasm_i_mfc0(&p, K1, C0_EPC); /* cp0 delay */ 4378c2ecf20Sopenharmony_ci uasm_i_tlbwr(&p); /* cp0 delay */ 4388c2ecf20Sopenharmony_ci uasm_i_jr(&p, K1); 4398c2ecf20Sopenharmony_ci uasm_i_rfe(&p); /* branch delay */ 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci if (p > tlb_handler + 32) 4428c2ecf20Sopenharmony_ci panic("TLB refill handler space exceeded"); 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci pr_debug("Wrote TLB refill handler (%u instructions).\n", 4458c2ecf20Sopenharmony_ci (unsigned int)(p - tlb_handler)); 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci memcpy((void *)ebase, tlb_handler, 0x80); 4488c2ecf20Sopenharmony_ci local_flush_icache_range(ebase, ebase + 0x80); 4498c2ecf20Sopenharmony_ci dump_handler("r3000_tlb_refill", (u32 *)ebase, (u32 *)(ebase + 0x80)); 4508c2ecf20Sopenharmony_ci} 4518c2ecf20Sopenharmony_ci#endif /* CONFIG_MIPS_PGD_C0_CONTEXT */ 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci/* 4548c2ecf20Sopenharmony_ci * The R4000 TLB handler is much more complicated. We have two 4558c2ecf20Sopenharmony_ci * consecutive handler areas with 32 instructions space each. 4568c2ecf20Sopenharmony_ci * Since they aren't used at the same time, we can overflow in the 4578c2ecf20Sopenharmony_ci * other one.To keep things simple, we first assume linear space, 4588c2ecf20Sopenharmony_ci * then we relocate it to the final handler layout as needed. 4598c2ecf20Sopenharmony_ci */ 4608c2ecf20Sopenharmony_cistatic u32 final_handler[64]; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci/* 4638c2ecf20Sopenharmony_ci * Hazards 4648c2ecf20Sopenharmony_ci * 4658c2ecf20Sopenharmony_ci * From the IDT errata for the QED RM5230 (Nevada), processor revision 1.0: 4668c2ecf20Sopenharmony_ci * 2. A timing hazard exists for the TLBP instruction. 4678c2ecf20Sopenharmony_ci * 4688c2ecf20Sopenharmony_ci * stalling_instruction 4698c2ecf20Sopenharmony_ci * TLBP 4708c2ecf20Sopenharmony_ci * 4718c2ecf20Sopenharmony_ci * The JTLB is being read for the TLBP throughout the stall generated by the 4728c2ecf20Sopenharmony_ci * previous instruction. This is not really correct as the stalling instruction 4738c2ecf20Sopenharmony_ci * can modify the address used to access the JTLB. The failure symptom is that 4748c2ecf20Sopenharmony_ci * the TLBP instruction will use an address created for the stalling instruction 4758c2ecf20Sopenharmony_ci * and not the address held in C0_ENHI and thus report the wrong results. 4768c2ecf20Sopenharmony_ci * 4778c2ecf20Sopenharmony_ci * The software work-around is to not allow the instruction preceding the TLBP 4788c2ecf20Sopenharmony_ci * to stall - make it an NOP or some other instruction guaranteed not to stall. 4798c2ecf20Sopenharmony_ci * 4808c2ecf20Sopenharmony_ci * Errata 2 will not be fixed. This errata is also on the R5000. 4818c2ecf20Sopenharmony_ci * 4828c2ecf20Sopenharmony_ci * As if we MIPS hackers wouldn't know how to nop pipelines happy ... 4838c2ecf20Sopenharmony_ci */ 4848c2ecf20Sopenharmony_cistatic void __maybe_unused build_tlb_probe_entry(u32 **p) 4858c2ecf20Sopenharmony_ci{ 4868c2ecf20Sopenharmony_ci switch (current_cpu_type()) { 4878c2ecf20Sopenharmony_ci /* Found by experiment: R4600 v2.0/R4700 needs this, too. */ 4888c2ecf20Sopenharmony_ci case CPU_R4600: 4898c2ecf20Sopenharmony_ci case CPU_R4700: 4908c2ecf20Sopenharmony_ci case CPU_R5000: 4918c2ecf20Sopenharmony_ci case CPU_NEVADA: 4928c2ecf20Sopenharmony_ci uasm_i_nop(p); 4938c2ecf20Sopenharmony_ci uasm_i_tlbp(p); 4948c2ecf20Sopenharmony_ci break; 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci default: 4978c2ecf20Sopenharmony_ci uasm_i_tlbp(p); 4988c2ecf20Sopenharmony_ci break; 4998c2ecf20Sopenharmony_ci } 5008c2ecf20Sopenharmony_ci} 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_civoid build_tlb_write_entry(u32 **p, struct uasm_label **l, 5038c2ecf20Sopenharmony_ci struct uasm_reloc **r, 5048c2ecf20Sopenharmony_ci enum tlb_write_entry wmode) 5058c2ecf20Sopenharmony_ci{ 5068c2ecf20Sopenharmony_ci void(*tlbw)(u32 **) = NULL; 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci switch (wmode) { 5098c2ecf20Sopenharmony_ci case tlb_random: tlbw = uasm_i_tlbwr; break; 5108c2ecf20Sopenharmony_ci case tlb_indexed: tlbw = uasm_i_tlbwi; break; 5118c2ecf20Sopenharmony_ci } 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci if (cpu_has_mips_r2_r6) { 5148c2ecf20Sopenharmony_ci if (cpu_has_mips_r2_exec_hazard) 5158c2ecf20Sopenharmony_ci uasm_i_ehb(p); 5168c2ecf20Sopenharmony_ci tlbw(p); 5178c2ecf20Sopenharmony_ci return; 5188c2ecf20Sopenharmony_ci } 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci switch (current_cpu_type()) { 5218c2ecf20Sopenharmony_ci case CPU_R4000PC: 5228c2ecf20Sopenharmony_ci case CPU_R4000SC: 5238c2ecf20Sopenharmony_ci case CPU_R4000MC: 5248c2ecf20Sopenharmony_ci case CPU_R4400PC: 5258c2ecf20Sopenharmony_ci case CPU_R4400SC: 5268c2ecf20Sopenharmony_ci case CPU_R4400MC: 5278c2ecf20Sopenharmony_ci /* 5288c2ecf20Sopenharmony_ci * This branch uses up a mtc0 hazard nop slot and saves 5298c2ecf20Sopenharmony_ci * two nops after the tlbw instruction. 5308c2ecf20Sopenharmony_ci */ 5318c2ecf20Sopenharmony_ci uasm_bgezl_hazard(p, r, hazard_instance); 5328c2ecf20Sopenharmony_ci tlbw(p); 5338c2ecf20Sopenharmony_ci uasm_bgezl_label(l, p, hazard_instance); 5348c2ecf20Sopenharmony_ci hazard_instance++; 5358c2ecf20Sopenharmony_ci uasm_i_nop(p); 5368c2ecf20Sopenharmony_ci break; 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci case CPU_R4600: 5398c2ecf20Sopenharmony_ci case CPU_R4700: 5408c2ecf20Sopenharmony_ci uasm_i_nop(p); 5418c2ecf20Sopenharmony_ci tlbw(p); 5428c2ecf20Sopenharmony_ci uasm_i_nop(p); 5438c2ecf20Sopenharmony_ci break; 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci case CPU_R5000: 5468c2ecf20Sopenharmony_ci case CPU_NEVADA: 5478c2ecf20Sopenharmony_ci uasm_i_nop(p); /* QED specifies 2 nops hazard */ 5488c2ecf20Sopenharmony_ci uasm_i_nop(p); /* QED specifies 2 nops hazard */ 5498c2ecf20Sopenharmony_ci tlbw(p); 5508c2ecf20Sopenharmony_ci break; 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci case CPU_5KC: 5538c2ecf20Sopenharmony_ci case CPU_TX49XX: 5548c2ecf20Sopenharmony_ci case CPU_PR4450: 5558c2ecf20Sopenharmony_ci case CPU_XLR: 5568c2ecf20Sopenharmony_ci uasm_i_nop(p); 5578c2ecf20Sopenharmony_ci tlbw(p); 5588c2ecf20Sopenharmony_ci break; 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci case CPU_R10000: 5618c2ecf20Sopenharmony_ci case CPU_R12000: 5628c2ecf20Sopenharmony_ci case CPU_R14000: 5638c2ecf20Sopenharmony_ci case CPU_R16000: 5648c2ecf20Sopenharmony_ci case CPU_4KC: 5658c2ecf20Sopenharmony_ci case CPU_4KEC: 5668c2ecf20Sopenharmony_ci case CPU_M14KC: 5678c2ecf20Sopenharmony_ci case CPU_M14KEC: 5688c2ecf20Sopenharmony_ci case CPU_SB1: 5698c2ecf20Sopenharmony_ci case CPU_SB1A: 5708c2ecf20Sopenharmony_ci case CPU_4KSC: 5718c2ecf20Sopenharmony_ci case CPU_20KC: 5728c2ecf20Sopenharmony_ci case CPU_25KF: 5738c2ecf20Sopenharmony_ci case CPU_BMIPS32: 5748c2ecf20Sopenharmony_ci case CPU_BMIPS3300: 5758c2ecf20Sopenharmony_ci case CPU_BMIPS4350: 5768c2ecf20Sopenharmony_ci case CPU_BMIPS4380: 5778c2ecf20Sopenharmony_ci case CPU_BMIPS5000: 5788c2ecf20Sopenharmony_ci case CPU_LOONGSON2EF: 5798c2ecf20Sopenharmony_ci case CPU_LOONGSON64: 5808c2ecf20Sopenharmony_ci case CPU_R5500: 5818c2ecf20Sopenharmony_ci if (m4kc_tlbp_war()) 5828c2ecf20Sopenharmony_ci uasm_i_nop(p); 5838c2ecf20Sopenharmony_ci fallthrough; 5848c2ecf20Sopenharmony_ci case CPU_ALCHEMY: 5858c2ecf20Sopenharmony_ci tlbw(p); 5868c2ecf20Sopenharmony_ci break; 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci case CPU_RM7000: 5898c2ecf20Sopenharmony_ci uasm_i_nop(p); 5908c2ecf20Sopenharmony_ci uasm_i_nop(p); 5918c2ecf20Sopenharmony_ci uasm_i_nop(p); 5928c2ecf20Sopenharmony_ci uasm_i_nop(p); 5938c2ecf20Sopenharmony_ci tlbw(p); 5948c2ecf20Sopenharmony_ci break; 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci case CPU_VR4111: 5978c2ecf20Sopenharmony_ci case CPU_VR4121: 5988c2ecf20Sopenharmony_ci case CPU_VR4122: 5998c2ecf20Sopenharmony_ci case CPU_VR4181: 6008c2ecf20Sopenharmony_ci case CPU_VR4181A: 6018c2ecf20Sopenharmony_ci uasm_i_nop(p); 6028c2ecf20Sopenharmony_ci uasm_i_nop(p); 6038c2ecf20Sopenharmony_ci tlbw(p); 6048c2ecf20Sopenharmony_ci uasm_i_nop(p); 6058c2ecf20Sopenharmony_ci uasm_i_nop(p); 6068c2ecf20Sopenharmony_ci break; 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci case CPU_VR4131: 6098c2ecf20Sopenharmony_ci case CPU_VR4133: 6108c2ecf20Sopenharmony_ci uasm_i_nop(p); 6118c2ecf20Sopenharmony_ci uasm_i_nop(p); 6128c2ecf20Sopenharmony_ci tlbw(p); 6138c2ecf20Sopenharmony_ci break; 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci case CPU_XBURST: 6168c2ecf20Sopenharmony_ci tlbw(p); 6178c2ecf20Sopenharmony_ci uasm_i_nop(p); 6188c2ecf20Sopenharmony_ci break; 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci default: 6218c2ecf20Sopenharmony_ci panic("No TLB refill handler yet (CPU type: %d)", 6228c2ecf20Sopenharmony_ci current_cpu_type()); 6238c2ecf20Sopenharmony_ci break; 6248c2ecf20Sopenharmony_ci } 6258c2ecf20Sopenharmony_ci} 6268c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(build_tlb_write_entry); 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_cistatic __maybe_unused void build_convert_pte_to_entrylo(u32 **p, 6298c2ecf20Sopenharmony_ci unsigned int reg) 6308c2ecf20Sopenharmony_ci{ 6318c2ecf20Sopenharmony_ci if (_PAGE_GLOBAL_SHIFT == 0) { 6328c2ecf20Sopenharmony_ci /* pte_t is already in EntryLo format */ 6338c2ecf20Sopenharmony_ci return; 6348c2ecf20Sopenharmony_ci } 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci if (cpu_has_rixi && _PAGE_NO_EXEC != 0) { 6378c2ecf20Sopenharmony_ci if (fill_includes_sw_bits) { 6388c2ecf20Sopenharmony_ci UASM_i_ROTR(p, reg, reg, ilog2(_PAGE_GLOBAL)); 6398c2ecf20Sopenharmony_ci } else { 6408c2ecf20Sopenharmony_ci UASM_i_SRL(p, reg, reg, ilog2(_PAGE_NO_EXEC)); 6418c2ecf20Sopenharmony_ci UASM_i_ROTR(p, reg, reg, 6428c2ecf20Sopenharmony_ci ilog2(_PAGE_GLOBAL) - ilog2(_PAGE_NO_EXEC)); 6438c2ecf20Sopenharmony_ci } 6448c2ecf20Sopenharmony_ci } else { 6458c2ecf20Sopenharmony_ci#ifdef CONFIG_PHYS_ADDR_T_64BIT 6468c2ecf20Sopenharmony_ci uasm_i_dsrl_safe(p, reg, reg, ilog2(_PAGE_GLOBAL)); 6478c2ecf20Sopenharmony_ci#else 6488c2ecf20Sopenharmony_ci UASM_i_SRL(p, reg, reg, ilog2(_PAGE_GLOBAL)); 6498c2ecf20Sopenharmony_ci#endif 6508c2ecf20Sopenharmony_ci } 6518c2ecf20Sopenharmony_ci} 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci#ifdef CONFIG_MIPS_HUGE_TLB_SUPPORT 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_cistatic void build_restore_pagemask(u32 **p, struct uasm_reloc **r, 6568c2ecf20Sopenharmony_ci unsigned int tmp, enum label_id lid, 6578c2ecf20Sopenharmony_ci int restore_scratch) 6588c2ecf20Sopenharmony_ci{ 6598c2ecf20Sopenharmony_ci if (restore_scratch) { 6608c2ecf20Sopenharmony_ci /* 6618c2ecf20Sopenharmony_ci * Ensure the MFC0 below observes the value written to the 6628c2ecf20Sopenharmony_ci * KScratch register by the prior MTC0. 6638c2ecf20Sopenharmony_ci */ 6648c2ecf20Sopenharmony_ci if (scratch_reg >= 0) 6658c2ecf20Sopenharmony_ci uasm_i_ehb(p); 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci /* Reset default page size */ 6688c2ecf20Sopenharmony_ci if (PM_DEFAULT_MASK >> 16) { 6698c2ecf20Sopenharmony_ci uasm_i_lui(p, tmp, PM_DEFAULT_MASK >> 16); 6708c2ecf20Sopenharmony_ci uasm_i_ori(p, tmp, tmp, PM_DEFAULT_MASK & 0xffff); 6718c2ecf20Sopenharmony_ci uasm_i_mtc0(p, tmp, C0_PAGEMASK); 6728c2ecf20Sopenharmony_ci uasm_il_b(p, r, lid); 6738c2ecf20Sopenharmony_ci } else if (PM_DEFAULT_MASK) { 6748c2ecf20Sopenharmony_ci uasm_i_ori(p, tmp, 0, PM_DEFAULT_MASK); 6758c2ecf20Sopenharmony_ci uasm_i_mtc0(p, tmp, C0_PAGEMASK); 6768c2ecf20Sopenharmony_ci uasm_il_b(p, r, lid); 6778c2ecf20Sopenharmony_ci } else { 6788c2ecf20Sopenharmony_ci uasm_i_mtc0(p, 0, C0_PAGEMASK); 6798c2ecf20Sopenharmony_ci uasm_il_b(p, r, lid); 6808c2ecf20Sopenharmony_ci } 6818c2ecf20Sopenharmony_ci if (scratch_reg >= 0) 6828c2ecf20Sopenharmony_ci UASM_i_MFC0(p, 1, c0_kscratch(), scratch_reg); 6838c2ecf20Sopenharmony_ci else 6848c2ecf20Sopenharmony_ci UASM_i_LW(p, 1, scratchpad_offset(0), 0); 6858c2ecf20Sopenharmony_ci } else { 6868c2ecf20Sopenharmony_ci /* Reset default page size */ 6878c2ecf20Sopenharmony_ci if (PM_DEFAULT_MASK >> 16) { 6888c2ecf20Sopenharmony_ci uasm_i_lui(p, tmp, PM_DEFAULT_MASK >> 16); 6898c2ecf20Sopenharmony_ci uasm_i_ori(p, tmp, tmp, PM_DEFAULT_MASK & 0xffff); 6908c2ecf20Sopenharmony_ci uasm_il_b(p, r, lid); 6918c2ecf20Sopenharmony_ci uasm_i_mtc0(p, tmp, C0_PAGEMASK); 6928c2ecf20Sopenharmony_ci } else if (PM_DEFAULT_MASK) { 6938c2ecf20Sopenharmony_ci uasm_i_ori(p, tmp, 0, PM_DEFAULT_MASK); 6948c2ecf20Sopenharmony_ci uasm_il_b(p, r, lid); 6958c2ecf20Sopenharmony_ci uasm_i_mtc0(p, tmp, C0_PAGEMASK); 6968c2ecf20Sopenharmony_ci } else { 6978c2ecf20Sopenharmony_ci uasm_il_b(p, r, lid); 6988c2ecf20Sopenharmony_ci uasm_i_mtc0(p, 0, C0_PAGEMASK); 6998c2ecf20Sopenharmony_ci } 7008c2ecf20Sopenharmony_ci } 7018c2ecf20Sopenharmony_ci} 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_cistatic void build_huge_tlb_write_entry(u32 **p, struct uasm_label **l, 7048c2ecf20Sopenharmony_ci struct uasm_reloc **r, 7058c2ecf20Sopenharmony_ci unsigned int tmp, 7068c2ecf20Sopenharmony_ci enum tlb_write_entry wmode, 7078c2ecf20Sopenharmony_ci int restore_scratch) 7088c2ecf20Sopenharmony_ci{ 7098c2ecf20Sopenharmony_ci /* Set huge page tlb entry size */ 7108c2ecf20Sopenharmony_ci uasm_i_lui(p, tmp, PM_HUGE_MASK >> 16); 7118c2ecf20Sopenharmony_ci uasm_i_ori(p, tmp, tmp, PM_HUGE_MASK & 0xffff); 7128c2ecf20Sopenharmony_ci uasm_i_mtc0(p, tmp, C0_PAGEMASK); 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci build_tlb_write_entry(p, l, r, wmode); 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci build_restore_pagemask(p, r, tmp, label_leave, restore_scratch); 7178c2ecf20Sopenharmony_ci} 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci/* 7208c2ecf20Sopenharmony_ci * Check if Huge PTE is present, if so then jump to LABEL. 7218c2ecf20Sopenharmony_ci */ 7228c2ecf20Sopenharmony_cistatic void 7238c2ecf20Sopenharmony_cibuild_is_huge_pte(u32 **p, struct uasm_reloc **r, unsigned int tmp, 7248c2ecf20Sopenharmony_ci unsigned int pmd, int lid) 7258c2ecf20Sopenharmony_ci{ 7268c2ecf20Sopenharmony_ci UASM_i_LW(p, tmp, 0, pmd); 7278c2ecf20Sopenharmony_ci if (use_bbit_insns()) { 7288c2ecf20Sopenharmony_ci uasm_il_bbit1(p, r, tmp, ilog2(_PAGE_HUGE), lid); 7298c2ecf20Sopenharmony_ci } else { 7308c2ecf20Sopenharmony_ci uasm_i_andi(p, tmp, tmp, _PAGE_HUGE); 7318c2ecf20Sopenharmony_ci uasm_il_bnez(p, r, tmp, lid); 7328c2ecf20Sopenharmony_ci } 7338c2ecf20Sopenharmony_ci} 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_cistatic void build_huge_update_entries(u32 **p, unsigned int pte, 7368c2ecf20Sopenharmony_ci unsigned int tmp) 7378c2ecf20Sopenharmony_ci{ 7388c2ecf20Sopenharmony_ci int small_sequence; 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci /* 7418c2ecf20Sopenharmony_ci * A huge PTE describes an area the size of the 7428c2ecf20Sopenharmony_ci * configured huge page size. This is twice the 7438c2ecf20Sopenharmony_ci * of the large TLB entry size we intend to use. 7448c2ecf20Sopenharmony_ci * A TLB entry half the size of the configured 7458c2ecf20Sopenharmony_ci * huge page size is configured into entrylo0 7468c2ecf20Sopenharmony_ci * and entrylo1 to cover the contiguous huge PTE 7478c2ecf20Sopenharmony_ci * address space. 7488c2ecf20Sopenharmony_ci */ 7498c2ecf20Sopenharmony_ci small_sequence = (HPAGE_SIZE >> 7) < 0x10000; 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci /* We can clobber tmp. It isn't used after this.*/ 7528c2ecf20Sopenharmony_ci if (!small_sequence) 7538c2ecf20Sopenharmony_ci uasm_i_lui(p, tmp, HPAGE_SIZE >> (7 + 16)); 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci build_convert_pte_to_entrylo(p, pte); 7568c2ecf20Sopenharmony_ci UASM_i_MTC0(p, pte, C0_ENTRYLO0); /* load it */ 7578c2ecf20Sopenharmony_ci /* convert to entrylo1 */ 7588c2ecf20Sopenharmony_ci if (small_sequence) 7598c2ecf20Sopenharmony_ci UASM_i_ADDIU(p, pte, pte, HPAGE_SIZE >> 7); 7608c2ecf20Sopenharmony_ci else 7618c2ecf20Sopenharmony_ci UASM_i_ADDU(p, pte, pte, tmp); 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci UASM_i_MTC0(p, pte, C0_ENTRYLO1); /* load it */ 7648c2ecf20Sopenharmony_ci} 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_cistatic void build_huge_handler_tail(u32 **p, struct uasm_reloc **r, 7678c2ecf20Sopenharmony_ci struct uasm_label **l, 7688c2ecf20Sopenharmony_ci unsigned int pte, 7698c2ecf20Sopenharmony_ci unsigned int ptr, 7708c2ecf20Sopenharmony_ci unsigned int flush) 7718c2ecf20Sopenharmony_ci{ 7728c2ecf20Sopenharmony_ci#ifdef CONFIG_SMP 7738c2ecf20Sopenharmony_ci UASM_i_SC(p, pte, 0, ptr); 7748c2ecf20Sopenharmony_ci uasm_il_beqz(p, r, pte, label_tlb_huge_update); 7758c2ecf20Sopenharmony_ci UASM_i_LW(p, pte, 0, ptr); /* Needed because SC killed our PTE */ 7768c2ecf20Sopenharmony_ci#else 7778c2ecf20Sopenharmony_ci UASM_i_SW(p, pte, 0, ptr); 7788c2ecf20Sopenharmony_ci#endif 7798c2ecf20Sopenharmony_ci if (cpu_has_ftlb && flush) { 7808c2ecf20Sopenharmony_ci BUG_ON(!cpu_has_tlbinv); 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci UASM_i_MFC0(p, ptr, C0_ENTRYHI); 7838c2ecf20Sopenharmony_ci uasm_i_ori(p, ptr, ptr, MIPS_ENTRYHI_EHINV); 7848c2ecf20Sopenharmony_ci UASM_i_MTC0(p, ptr, C0_ENTRYHI); 7858c2ecf20Sopenharmony_ci build_tlb_write_entry(p, l, r, tlb_indexed); 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_ci uasm_i_xori(p, ptr, ptr, MIPS_ENTRYHI_EHINV); 7888c2ecf20Sopenharmony_ci UASM_i_MTC0(p, ptr, C0_ENTRYHI); 7898c2ecf20Sopenharmony_ci build_huge_update_entries(p, pte, ptr); 7908c2ecf20Sopenharmony_ci build_huge_tlb_write_entry(p, l, r, pte, tlb_random, 0); 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci return; 7938c2ecf20Sopenharmony_ci } 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci build_huge_update_entries(p, pte, ptr); 7968c2ecf20Sopenharmony_ci build_huge_tlb_write_entry(p, l, r, pte, tlb_indexed, 0); 7978c2ecf20Sopenharmony_ci} 7988c2ecf20Sopenharmony_ci#endif /* CONFIG_MIPS_HUGE_TLB_SUPPORT */ 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci#ifdef CONFIG_64BIT 8018c2ecf20Sopenharmony_ci/* 8028c2ecf20Sopenharmony_ci * TMP and PTR are scratch. 8038c2ecf20Sopenharmony_ci * TMP will be clobbered, PTR will hold the pmd entry. 8048c2ecf20Sopenharmony_ci */ 8058c2ecf20Sopenharmony_civoid build_get_pmde64(u32 **p, struct uasm_label **l, struct uasm_reloc **r, 8068c2ecf20Sopenharmony_ci unsigned int tmp, unsigned int ptr) 8078c2ecf20Sopenharmony_ci{ 8088c2ecf20Sopenharmony_ci#ifndef CONFIG_MIPS_PGD_C0_CONTEXT 8098c2ecf20Sopenharmony_ci long pgdc = (long)pgd_current; 8108c2ecf20Sopenharmony_ci#endif 8118c2ecf20Sopenharmony_ci /* 8128c2ecf20Sopenharmony_ci * The vmalloc handling is not in the hotpath. 8138c2ecf20Sopenharmony_ci */ 8148c2ecf20Sopenharmony_ci uasm_i_dmfc0(p, tmp, C0_BADVADDR); 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci if (check_for_high_segbits) { 8178c2ecf20Sopenharmony_ci /* 8188c2ecf20Sopenharmony_ci * The kernel currently implicitely assumes that the 8198c2ecf20Sopenharmony_ci * MIPS SEGBITS parameter for the processor is 8208c2ecf20Sopenharmony_ci * (PGDIR_SHIFT+PGDIR_BITS) or less, and will never 8218c2ecf20Sopenharmony_ci * allocate virtual addresses outside the maximum 8228c2ecf20Sopenharmony_ci * range for SEGBITS = (PGDIR_SHIFT+PGDIR_BITS). But 8238c2ecf20Sopenharmony_ci * that doesn't prevent user code from accessing the 8248c2ecf20Sopenharmony_ci * higher xuseg addresses. Here, we make sure that 8258c2ecf20Sopenharmony_ci * everything but the lower xuseg addresses goes down 8268c2ecf20Sopenharmony_ci * the module_alloc/vmalloc path. 8278c2ecf20Sopenharmony_ci */ 8288c2ecf20Sopenharmony_ci uasm_i_dsrl_safe(p, ptr, tmp, PGDIR_SHIFT + PGD_ORDER + PAGE_SHIFT - 3); 8298c2ecf20Sopenharmony_ci uasm_il_bnez(p, r, ptr, label_vmalloc); 8308c2ecf20Sopenharmony_ci } else { 8318c2ecf20Sopenharmony_ci uasm_il_bltz(p, r, tmp, label_vmalloc); 8328c2ecf20Sopenharmony_ci } 8338c2ecf20Sopenharmony_ci /* No uasm_i_nop needed here, since the next insn doesn't touch TMP. */ 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci if (pgd_reg != -1) { 8368c2ecf20Sopenharmony_ci /* pgd is in pgd_reg */ 8378c2ecf20Sopenharmony_ci if (cpu_has_ldpte) 8388c2ecf20Sopenharmony_ci UASM_i_MFC0(p, ptr, C0_PWBASE); 8398c2ecf20Sopenharmony_ci else 8408c2ecf20Sopenharmony_ci UASM_i_MFC0(p, ptr, c0_kscratch(), pgd_reg); 8418c2ecf20Sopenharmony_ci } else { 8428c2ecf20Sopenharmony_ci#if defined(CONFIG_MIPS_PGD_C0_CONTEXT) 8438c2ecf20Sopenharmony_ci /* 8448c2ecf20Sopenharmony_ci * &pgd << 11 stored in CONTEXT [23..63]. 8458c2ecf20Sopenharmony_ci */ 8468c2ecf20Sopenharmony_ci UASM_i_MFC0(p, ptr, C0_CONTEXT); 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci /* Clear lower 23 bits of context. */ 8498c2ecf20Sopenharmony_ci uasm_i_dins(p, ptr, 0, 0, 23); 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci /* 1 0 1 0 1 << 6 xkphys cached */ 8528c2ecf20Sopenharmony_ci uasm_i_ori(p, ptr, ptr, 0x540); 8538c2ecf20Sopenharmony_ci uasm_i_drotr(p, ptr, ptr, 11); 8548c2ecf20Sopenharmony_ci#elif defined(CONFIG_SMP) 8558c2ecf20Sopenharmony_ci UASM_i_CPUID_MFC0(p, ptr, SMP_CPUID_REG); 8568c2ecf20Sopenharmony_ci uasm_i_dsrl_safe(p, ptr, ptr, SMP_CPUID_PTRSHIFT); 8578c2ecf20Sopenharmony_ci UASM_i_LA_mostly(p, tmp, pgdc); 8588c2ecf20Sopenharmony_ci uasm_i_daddu(p, ptr, ptr, tmp); 8598c2ecf20Sopenharmony_ci uasm_i_dmfc0(p, tmp, C0_BADVADDR); 8608c2ecf20Sopenharmony_ci uasm_i_ld(p, ptr, uasm_rel_lo(pgdc), ptr); 8618c2ecf20Sopenharmony_ci#else 8628c2ecf20Sopenharmony_ci UASM_i_LA_mostly(p, ptr, pgdc); 8638c2ecf20Sopenharmony_ci uasm_i_ld(p, ptr, uasm_rel_lo(pgdc), ptr); 8648c2ecf20Sopenharmony_ci#endif 8658c2ecf20Sopenharmony_ci } 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci uasm_l_vmalloc_done(l, *p); 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci /* get pgd offset in bytes */ 8708c2ecf20Sopenharmony_ci uasm_i_dsrl_safe(p, tmp, tmp, PGDIR_SHIFT - 3); 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci uasm_i_andi(p, tmp, tmp, (PTRS_PER_PGD - 1)<<3); 8738c2ecf20Sopenharmony_ci uasm_i_daddu(p, ptr, ptr, tmp); /* add in pgd offset */ 8748c2ecf20Sopenharmony_ci#ifndef __PAGETABLE_PUD_FOLDED 8758c2ecf20Sopenharmony_ci uasm_i_dmfc0(p, tmp, C0_BADVADDR); /* get faulting address */ 8768c2ecf20Sopenharmony_ci uasm_i_ld(p, ptr, 0, ptr); /* get pud pointer */ 8778c2ecf20Sopenharmony_ci uasm_i_dsrl_safe(p, tmp, tmp, PUD_SHIFT - 3); /* get pud offset in bytes */ 8788c2ecf20Sopenharmony_ci uasm_i_andi(p, tmp, tmp, (PTRS_PER_PUD - 1) << 3); 8798c2ecf20Sopenharmony_ci uasm_i_daddu(p, ptr, ptr, tmp); /* add in pud offset */ 8808c2ecf20Sopenharmony_ci#endif 8818c2ecf20Sopenharmony_ci#ifndef __PAGETABLE_PMD_FOLDED 8828c2ecf20Sopenharmony_ci uasm_i_dmfc0(p, tmp, C0_BADVADDR); /* get faulting address */ 8838c2ecf20Sopenharmony_ci uasm_i_ld(p, ptr, 0, ptr); /* get pmd pointer */ 8848c2ecf20Sopenharmony_ci uasm_i_dsrl_safe(p, tmp, tmp, PMD_SHIFT-3); /* get pmd offset in bytes */ 8858c2ecf20Sopenharmony_ci uasm_i_andi(p, tmp, tmp, (PTRS_PER_PMD - 1)<<3); 8868c2ecf20Sopenharmony_ci uasm_i_daddu(p, ptr, ptr, tmp); /* add in pmd offset */ 8878c2ecf20Sopenharmony_ci#endif 8888c2ecf20Sopenharmony_ci} 8898c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(build_get_pmde64); 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci/* 8928c2ecf20Sopenharmony_ci * BVADDR is the faulting address, PTR is scratch. 8938c2ecf20Sopenharmony_ci * PTR will hold the pgd for vmalloc. 8948c2ecf20Sopenharmony_ci */ 8958c2ecf20Sopenharmony_cistatic void 8968c2ecf20Sopenharmony_cibuild_get_pgd_vmalloc64(u32 **p, struct uasm_label **l, struct uasm_reloc **r, 8978c2ecf20Sopenharmony_ci unsigned int bvaddr, unsigned int ptr, 8988c2ecf20Sopenharmony_ci enum vmalloc64_mode mode) 8998c2ecf20Sopenharmony_ci{ 9008c2ecf20Sopenharmony_ci long swpd = (long)swapper_pg_dir; 9018c2ecf20Sopenharmony_ci int single_insn_swpd; 9028c2ecf20Sopenharmony_ci int did_vmalloc_branch = 0; 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_ci single_insn_swpd = uasm_in_compat_space_p(swpd) && !uasm_rel_lo(swpd); 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_ci uasm_l_vmalloc(l, *p); 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_ci if (mode != not_refill && check_for_high_segbits) { 9098c2ecf20Sopenharmony_ci if (single_insn_swpd) { 9108c2ecf20Sopenharmony_ci uasm_il_bltz(p, r, bvaddr, label_vmalloc_done); 9118c2ecf20Sopenharmony_ci uasm_i_lui(p, ptr, uasm_rel_hi(swpd)); 9128c2ecf20Sopenharmony_ci did_vmalloc_branch = 1; 9138c2ecf20Sopenharmony_ci /* fall through */ 9148c2ecf20Sopenharmony_ci } else { 9158c2ecf20Sopenharmony_ci uasm_il_bgez(p, r, bvaddr, label_large_segbits_fault); 9168c2ecf20Sopenharmony_ci } 9178c2ecf20Sopenharmony_ci } 9188c2ecf20Sopenharmony_ci if (!did_vmalloc_branch) { 9198c2ecf20Sopenharmony_ci if (single_insn_swpd) { 9208c2ecf20Sopenharmony_ci uasm_il_b(p, r, label_vmalloc_done); 9218c2ecf20Sopenharmony_ci uasm_i_lui(p, ptr, uasm_rel_hi(swpd)); 9228c2ecf20Sopenharmony_ci } else { 9238c2ecf20Sopenharmony_ci UASM_i_LA_mostly(p, ptr, swpd); 9248c2ecf20Sopenharmony_ci uasm_il_b(p, r, label_vmalloc_done); 9258c2ecf20Sopenharmony_ci if (uasm_in_compat_space_p(swpd)) 9268c2ecf20Sopenharmony_ci uasm_i_addiu(p, ptr, ptr, uasm_rel_lo(swpd)); 9278c2ecf20Sopenharmony_ci else 9288c2ecf20Sopenharmony_ci uasm_i_daddiu(p, ptr, ptr, uasm_rel_lo(swpd)); 9298c2ecf20Sopenharmony_ci } 9308c2ecf20Sopenharmony_ci } 9318c2ecf20Sopenharmony_ci if (mode != not_refill && check_for_high_segbits) { 9328c2ecf20Sopenharmony_ci uasm_l_large_segbits_fault(l, *p); 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ci if (mode == refill_scratch && scratch_reg >= 0) 9358c2ecf20Sopenharmony_ci uasm_i_ehb(p); 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci /* 9388c2ecf20Sopenharmony_ci * We get here if we are an xsseg address, or if we are 9398c2ecf20Sopenharmony_ci * an xuseg address above (PGDIR_SHIFT+PGDIR_BITS) boundary. 9408c2ecf20Sopenharmony_ci * 9418c2ecf20Sopenharmony_ci * Ignoring xsseg (assume disabled so would generate 9428c2ecf20Sopenharmony_ci * (address errors?), the only remaining possibility 9438c2ecf20Sopenharmony_ci * is the upper xuseg addresses. On processors with 9448c2ecf20Sopenharmony_ci * TLB_SEGBITS <= PGDIR_SHIFT+PGDIR_BITS, these 9458c2ecf20Sopenharmony_ci * addresses would have taken an address error. We try 9468c2ecf20Sopenharmony_ci * to mimic that here by taking a load/istream page 9478c2ecf20Sopenharmony_ci * fault. 9488c2ecf20Sopenharmony_ci */ 9498c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_CPU_LOONGSON3_WORKAROUNDS)) 9508c2ecf20Sopenharmony_ci uasm_i_sync(p, 0); 9518c2ecf20Sopenharmony_ci UASM_i_LA(p, ptr, (unsigned long)tlb_do_page_fault_0); 9528c2ecf20Sopenharmony_ci uasm_i_jr(p, ptr); 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_ci if (mode == refill_scratch) { 9558c2ecf20Sopenharmony_ci if (scratch_reg >= 0) 9568c2ecf20Sopenharmony_ci UASM_i_MFC0(p, 1, c0_kscratch(), scratch_reg); 9578c2ecf20Sopenharmony_ci else 9588c2ecf20Sopenharmony_ci UASM_i_LW(p, 1, scratchpad_offset(0), 0); 9598c2ecf20Sopenharmony_ci } else { 9608c2ecf20Sopenharmony_ci uasm_i_nop(p); 9618c2ecf20Sopenharmony_ci } 9628c2ecf20Sopenharmony_ci } 9638c2ecf20Sopenharmony_ci} 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci#else /* !CONFIG_64BIT */ 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ci/* 9688c2ecf20Sopenharmony_ci * TMP and PTR are scratch. 9698c2ecf20Sopenharmony_ci * TMP will be clobbered, PTR will hold the pgd entry. 9708c2ecf20Sopenharmony_ci */ 9718c2ecf20Sopenharmony_civoid build_get_pgde32(u32 **p, unsigned int tmp, unsigned int ptr) 9728c2ecf20Sopenharmony_ci{ 9738c2ecf20Sopenharmony_ci if (pgd_reg != -1) { 9748c2ecf20Sopenharmony_ci /* pgd is in pgd_reg */ 9758c2ecf20Sopenharmony_ci uasm_i_mfc0(p, ptr, c0_kscratch(), pgd_reg); 9768c2ecf20Sopenharmony_ci uasm_i_mfc0(p, tmp, C0_BADVADDR); /* get faulting address */ 9778c2ecf20Sopenharmony_ci } else { 9788c2ecf20Sopenharmony_ci long pgdc = (long)pgd_current; 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci /* 32 bit SMP has smp_processor_id() stored in CONTEXT. */ 9818c2ecf20Sopenharmony_ci#ifdef CONFIG_SMP 9828c2ecf20Sopenharmony_ci uasm_i_mfc0(p, ptr, SMP_CPUID_REG); 9838c2ecf20Sopenharmony_ci UASM_i_LA_mostly(p, tmp, pgdc); 9848c2ecf20Sopenharmony_ci uasm_i_srl(p, ptr, ptr, SMP_CPUID_PTRSHIFT); 9858c2ecf20Sopenharmony_ci uasm_i_addu(p, ptr, tmp, ptr); 9868c2ecf20Sopenharmony_ci#else 9878c2ecf20Sopenharmony_ci UASM_i_LA_mostly(p, ptr, pgdc); 9888c2ecf20Sopenharmony_ci#endif 9898c2ecf20Sopenharmony_ci uasm_i_mfc0(p, tmp, C0_BADVADDR); /* get faulting address */ 9908c2ecf20Sopenharmony_ci uasm_i_lw(p, ptr, uasm_rel_lo(pgdc), ptr); 9918c2ecf20Sopenharmony_ci } 9928c2ecf20Sopenharmony_ci uasm_i_srl(p, tmp, tmp, PGDIR_SHIFT); /* get pgd only bits */ 9938c2ecf20Sopenharmony_ci uasm_i_sll(p, tmp, tmp, PGD_T_LOG2); 9948c2ecf20Sopenharmony_ci uasm_i_addu(p, ptr, ptr, tmp); /* add in pgd offset */ 9958c2ecf20Sopenharmony_ci} 9968c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(build_get_pgde32); 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_ci#endif /* !CONFIG_64BIT */ 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_cistatic void build_adjust_context(u32 **p, unsigned int ctx) 10018c2ecf20Sopenharmony_ci{ 10028c2ecf20Sopenharmony_ci unsigned int shift = 4 - (PTE_T_LOG2 + 1) + PAGE_SHIFT - 12; 10038c2ecf20Sopenharmony_ci unsigned int mask = (PTRS_PER_PTE / 2 - 1) << (PTE_T_LOG2 + 1); 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_ci switch (current_cpu_type()) { 10068c2ecf20Sopenharmony_ci case CPU_VR41XX: 10078c2ecf20Sopenharmony_ci case CPU_VR4111: 10088c2ecf20Sopenharmony_ci case CPU_VR4121: 10098c2ecf20Sopenharmony_ci case CPU_VR4122: 10108c2ecf20Sopenharmony_ci case CPU_VR4131: 10118c2ecf20Sopenharmony_ci case CPU_VR4181: 10128c2ecf20Sopenharmony_ci case CPU_VR4181A: 10138c2ecf20Sopenharmony_ci case CPU_VR4133: 10148c2ecf20Sopenharmony_ci shift += 2; 10158c2ecf20Sopenharmony_ci break; 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_ci default: 10188c2ecf20Sopenharmony_ci break; 10198c2ecf20Sopenharmony_ci } 10208c2ecf20Sopenharmony_ci 10218c2ecf20Sopenharmony_ci if (shift) 10228c2ecf20Sopenharmony_ci UASM_i_SRL(p, ctx, ctx, shift); 10238c2ecf20Sopenharmony_ci uasm_i_andi(p, ctx, ctx, mask); 10248c2ecf20Sopenharmony_ci} 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_civoid build_get_ptep(u32 **p, unsigned int tmp, unsigned int ptr) 10278c2ecf20Sopenharmony_ci{ 10288c2ecf20Sopenharmony_ci /* 10298c2ecf20Sopenharmony_ci * Bug workaround for the Nevada. It seems as if under certain 10308c2ecf20Sopenharmony_ci * circumstances the move from cp0_context might produce a 10318c2ecf20Sopenharmony_ci * bogus result when the mfc0 instruction and its consumer are 10328c2ecf20Sopenharmony_ci * in a different cacheline or a load instruction, probably any 10338c2ecf20Sopenharmony_ci * memory reference, is between them. 10348c2ecf20Sopenharmony_ci */ 10358c2ecf20Sopenharmony_ci switch (current_cpu_type()) { 10368c2ecf20Sopenharmony_ci case CPU_NEVADA: 10378c2ecf20Sopenharmony_ci UASM_i_LW(p, ptr, 0, ptr); 10388c2ecf20Sopenharmony_ci GET_CONTEXT(p, tmp); /* get context reg */ 10398c2ecf20Sopenharmony_ci break; 10408c2ecf20Sopenharmony_ci 10418c2ecf20Sopenharmony_ci default: 10428c2ecf20Sopenharmony_ci GET_CONTEXT(p, tmp); /* get context reg */ 10438c2ecf20Sopenharmony_ci UASM_i_LW(p, ptr, 0, ptr); 10448c2ecf20Sopenharmony_ci break; 10458c2ecf20Sopenharmony_ci } 10468c2ecf20Sopenharmony_ci 10478c2ecf20Sopenharmony_ci build_adjust_context(p, tmp); 10488c2ecf20Sopenharmony_ci UASM_i_ADDU(p, ptr, ptr, tmp); /* add in offset */ 10498c2ecf20Sopenharmony_ci} 10508c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(build_get_ptep); 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_civoid build_update_entries(u32 **p, unsigned int tmp, unsigned int ptep) 10538c2ecf20Sopenharmony_ci{ 10548c2ecf20Sopenharmony_ci int pte_off_even = 0; 10558c2ecf20Sopenharmony_ci int pte_off_odd = sizeof(pte_t); 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci#if defined(CONFIG_CPU_MIPS32) && defined(CONFIG_PHYS_ADDR_T_64BIT) 10588c2ecf20Sopenharmony_ci /* The low 32 bits of EntryLo is stored in pte_high */ 10598c2ecf20Sopenharmony_ci pte_off_even += offsetof(pte_t, pte_high); 10608c2ecf20Sopenharmony_ci pte_off_odd += offsetof(pte_t, pte_high); 10618c2ecf20Sopenharmony_ci#endif 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_XPA)) { 10648c2ecf20Sopenharmony_ci uasm_i_lw(p, tmp, pte_off_even, ptep); /* even pte */ 10658c2ecf20Sopenharmony_ci UASM_i_ROTR(p, tmp, tmp, ilog2(_PAGE_GLOBAL)); 10668c2ecf20Sopenharmony_ci UASM_i_MTC0(p, tmp, C0_ENTRYLO0); 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_ci if (cpu_has_xpa && !mips_xpa_disabled) { 10698c2ecf20Sopenharmony_ci uasm_i_lw(p, tmp, 0, ptep); 10708c2ecf20Sopenharmony_ci uasm_i_ext(p, tmp, tmp, 0, 24); 10718c2ecf20Sopenharmony_ci uasm_i_mthc0(p, tmp, C0_ENTRYLO0); 10728c2ecf20Sopenharmony_ci } 10738c2ecf20Sopenharmony_ci 10748c2ecf20Sopenharmony_ci uasm_i_lw(p, tmp, pte_off_odd, ptep); /* odd pte */ 10758c2ecf20Sopenharmony_ci UASM_i_ROTR(p, tmp, tmp, ilog2(_PAGE_GLOBAL)); 10768c2ecf20Sopenharmony_ci UASM_i_MTC0(p, tmp, C0_ENTRYLO1); 10778c2ecf20Sopenharmony_ci 10788c2ecf20Sopenharmony_ci if (cpu_has_xpa && !mips_xpa_disabled) { 10798c2ecf20Sopenharmony_ci uasm_i_lw(p, tmp, sizeof(pte_t), ptep); 10808c2ecf20Sopenharmony_ci uasm_i_ext(p, tmp, tmp, 0, 24); 10818c2ecf20Sopenharmony_ci uasm_i_mthc0(p, tmp, C0_ENTRYLO1); 10828c2ecf20Sopenharmony_ci } 10838c2ecf20Sopenharmony_ci return; 10848c2ecf20Sopenharmony_ci } 10858c2ecf20Sopenharmony_ci 10868c2ecf20Sopenharmony_ci UASM_i_LW(p, tmp, pte_off_even, ptep); /* get even pte */ 10878c2ecf20Sopenharmony_ci UASM_i_LW(p, ptep, pte_off_odd, ptep); /* get odd pte */ 10888c2ecf20Sopenharmony_ci if (r45k_bvahwbug()) 10898c2ecf20Sopenharmony_ci build_tlb_probe_entry(p); 10908c2ecf20Sopenharmony_ci build_convert_pte_to_entrylo(p, tmp); 10918c2ecf20Sopenharmony_ci if (r4k_250MHZhwbug()) 10928c2ecf20Sopenharmony_ci UASM_i_MTC0(p, 0, C0_ENTRYLO0); 10938c2ecf20Sopenharmony_ci UASM_i_MTC0(p, tmp, C0_ENTRYLO0); /* load it */ 10948c2ecf20Sopenharmony_ci build_convert_pte_to_entrylo(p, ptep); 10958c2ecf20Sopenharmony_ci if (r45k_bvahwbug()) 10968c2ecf20Sopenharmony_ci uasm_i_mfc0(p, tmp, C0_INDEX); 10978c2ecf20Sopenharmony_ci if (r4k_250MHZhwbug()) 10988c2ecf20Sopenharmony_ci UASM_i_MTC0(p, 0, C0_ENTRYLO1); 10998c2ecf20Sopenharmony_ci UASM_i_MTC0(p, ptep, C0_ENTRYLO1); /* load it */ 11008c2ecf20Sopenharmony_ci} 11018c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(build_update_entries); 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_cistruct mips_huge_tlb_info { 11048c2ecf20Sopenharmony_ci int huge_pte; 11058c2ecf20Sopenharmony_ci int restore_scratch; 11068c2ecf20Sopenharmony_ci bool need_reload_pte; 11078c2ecf20Sopenharmony_ci}; 11088c2ecf20Sopenharmony_ci 11098c2ecf20Sopenharmony_cistatic struct mips_huge_tlb_info 11108c2ecf20Sopenharmony_cibuild_fast_tlb_refill_handler (u32 **p, struct uasm_label **l, 11118c2ecf20Sopenharmony_ci struct uasm_reloc **r, unsigned int tmp, 11128c2ecf20Sopenharmony_ci unsigned int ptr, int c0_scratch_reg) 11138c2ecf20Sopenharmony_ci{ 11148c2ecf20Sopenharmony_ci struct mips_huge_tlb_info rv; 11158c2ecf20Sopenharmony_ci unsigned int even, odd; 11168c2ecf20Sopenharmony_ci int vmalloc_branch_delay_filled = 0; 11178c2ecf20Sopenharmony_ci const int scratch = 1; /* Our extra working register */ 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_ci rv.huge_pte = scratch; 11208c2ecf20Sopenharmony_ci rv.restore_scratch = 0; 11218c2ecf20Sopenharmony_ci rv.need_reload_pte = false; 11228c2ecf20Sopenharmony_ci 11238c2ecf20Sopenharmony_ci if (check_for_high_segbits) { 11248c2ecf20Sopenharmony_ci UASM_i_MFC0(p, tmp, C0_BADVADDR); 11258c2ecf20Sopenharmony_ci 11268c2ecf20Sopenharmony_ci if (pgd_reg != -1) 11278c2ecf20Sopenharmony_ci UASM_i_MFC0(p, ptr, c0_kscratch(), pgd_reg); 11288c2ecf20Sopenharmony_ci else 11298c2ecf20Sopenharmony_ci UASM_i_MFC0(p, ptr, C0_CONTEXT); 11308c2ecf20Sopenharmony_ci 11318c2ecf20Sopenharmony_ci if (c0_scratch_reg >= 0) 11328c2ecf20Sopenharmony_ci UASM_i_MTC0(p, scratch, c0_kscratch(), c0_scratch_reg); 11338c2ecf20Sopenharmony_ci else 11348c2ecf20Sopenharmony_ci UASM_i_SW(p, scratch, scratchpad_offset(0), 0); 11358c2ecf20Sopenharmony_ci 11368c2ecf20Sopenharmony_ci uasm_i_dsrl_safe(p, scratch, tmp, 11378c2ecf20Sopenharmony_ci PGDIR_SHIFT + PGD_ORDER + PAGE_SHIFT - 3); 11388c2ecf20Sopenharmony_ci uasm_il_bnez(p, r, scratch, label_vmalloc); 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_ci if (pgd_reg == -1) { 11418c2ecf20Sopenharmony_ci vmalloc_branch_delay_filled = 1; 11428c2ecf20Sopenharmony_ci /* Clear lower 23 bits of context. */ 11438c2ecf20Sopenharmony_ci uasm_i_dins(p, ptr, 0, 0, 23); 11448c2ecf20Sopenharmony_ci } 11458c2ecf20Sopenharmony_ci } else { 11468c2ecf20Sopenharmony_ci if (pgd_reg != -1) 11478c2ecf20Sopenharmony_ci UASM_i_MFC0(p, ptr, c0_kscratch(), pgd_reg); 11488c2ecf20Sopenharmony_ci else 11498c2ecf20Sopenharmony_ci UASM_i_MFC0(p, ptr, C0_CONTEXT); 11508c2ecf20Sopenharmony_ci 11518c2ecf20Sopenharmony_ci UASM_i_MFC0(p, tmp, C0_BADVADDR); 11528c2ecf20Sopenharmony_ci 11538c2ecf20Sopenharmony_ci if (c0_scratch_reg >= 0) 11548c2ecf20Sopenharmony_ci UASM_i_MTC0(p, scratch, c0_kscratch(), c0_scratch_reg); 11558c2ecf20Sopenharmony_ci else 11568c2ecf20Sopenharmony_ci UASM_i_SW(p, scratch, scratchpad_offset(0), 0); 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_ci if (pgd_reg == -1) 11598c2ecf20Sopenharmony_ci /* Clear lower 23 bits of context. */ 11608c2ecf20Sopenharmony_ci uasm_i_dins(p, ptr, 0, 0, 23); 11618c2ecf20Sopenharmony_ci 11628c2ecf20Sopenharmony_ci uasm_il_bltz(p, r, tmp, label_vmalloc); 11638c2ecf20Sopenharmony_ci } 11648c2ecf20Sopenharmony_ci 11658c2ecf20Sopenharmony_ci if (pgd_reg == -1) { 11668c2ecf20Sopenharmony_ci vmalloc_branch_delay_filled = 1; 11678c2ecf20Sopenharmony_ci /* 1 0 1 0 1 << 6 xkphys cached */ 11688c2ecf20Sopenharmony_ci uasm_i_ori(p, ptr, ptr, 0x540); 11698c2ecf20Sopenharmony_ci uasm_i_drotr(p, ptr, ptr, 11); 11708c2ecf20Sopenharmony_ci } 11718c2ecf20Sopenharmony_ci 11728c2ecf20Sopenharmony_ci#ifdef __PAGETABLE_PMD_FOLDED 11738c2ecf20Sopenharmony_ci#define LOC_PTEP scratch 11748c2ecf20Sopenharmony_ci#else 11758c2ecf20Sopenharmony_ci#define LOC_PTEP ptr 11768c2ecf20Sopenharmony_ci#endif 11778c2ecf20Sopenharmony_ci 11788c2ecf20Sopenharmony_ci if (!vmalloc_branch_delay_filled) 11798c2ecf20Sopenharmony_ci /* get pgd offset in bytes */ 11808c2ecf20Sopenharmony_ci uasm_i_dsrl_safe(p, scratch, tmp, PGDIR_SHIFT - 3); 11818c2ecf20Sopenharmony_ci 11828c2ecf20Sopenharmony_ci uasm_l_vmalloc_done(l, *p); 11838c2ecf20Sopenharmony_ci 11848c2ecf20Sopenharmony_ci /* 11858c2ecf20Sopenharmony_ci * tmp ptr 11868c2ecf20Sopenharmony_ci * fall-through case = badvaddr *pgd_current 11878c2ecf20Sopenharmony_ci * vmalloc case = badvaddr swapper_pg_dir 11888c2ecf20Sopenharmony_ci */ 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_ci if (vmalloc_branch_delay_filled) 11918c2ecf20Sopenharmony_ci /* get pgd offset in bytes */ 11928c2ecf20Sopenharmony_ci uasm_i_dsrl_safe(p, scratch, tmp, PGDIR_SHIFT - 3); 11938c2ecf20Sopenharmony_ci 11948c2ecf20Sopenharmony_ci#ifdef __PAGETABLE_PMD_FOLDED 11958c2ecf20Sopenharmony_ci GET_CONTEXT(p, tmp); /* get context reg */ 11968c2ecf20Sopenharmony_ci#endif 11978c2ecf20Sopenharmony_ci uasm_i_andi(p, scratch, scratch, (PTRS_PER_PGD - 1) << 3); 11988c2ecf20Sopenharmony_ci 11998c2ecf20Sopenharmony_ci if (use_lwx_insns()) { 12008c2ecf20Sopenharmony_ci UASM_i_LWX(p, LOC_PTEP, scratch, ptr); 12018c2ecf20Sopenharmony_ci } else { 12028c2ecf20Sopenharmony_ci uasm_i_daddu(p, ptr, ptr, scratch); /* add in pgd offset */ 12038c2ecf20Sopenharmony_ci uasm_i_ld(p, LOC_PTEP, 0, ptr); /* get pmd pointer */ 12048c2ecf20Sopenharmony_ci } 12058c2ecf20Sopenharmony_ci 12068c2ecf20Sopenharmony_ci#ifndef __PAGETABLE_PUD_FOLDED 12078c2ecf20Sopenharmony_ci /* get pud offset in bytes */ 12088c2ecf20Sopenharmony_ci uasm_i_dsrl_safe(p, scratch, tmp, PUD_SHIFT - 3); 12098c2ecf20Sopenharmony_ci uasm_i_andi(p, scratch, scratch, (PTRS_PER_PUD - 1) << 3); 12108c2ecf20Sopenharmony_ci 12118c2ecf20Sopenharmony_ci if (use_lwx_insns()) { 12128c2ecf20Sopenharmony_ci UASM_i_LWX(p, ptr, scratch, ptr); 12138c2ecf20Sopenharmony_ci } else { 12148c2ecf20Sopenharmony_ci uasm_i_daddu(p, ptr, ptr, scratch); /* add in pmd offset */ 12158c2ecf20Sopenharmony_ci UASM_i_LW(p, ptr, 0, ptr); 12168c2ecf20Sopenharmony_ci } 12178c2ecf20Sopenharmony_ci /* ptr contains a pointer to PMD entry */ 12188c2ecf20Sopenharmony_ci /* tmp contains the address */ 12198c2ecf20Sopenharmony_ci#endif 12208c2ecf20Sopenharmony_ci 12218c2ecf20Sopenharmony_ci#ifndef __PAGETABLE_PMD_FOLDED 12228c2ecf20Sopenharmony_ci /* get pmd offset in bytes */ 12238c2ecf20Sopenharmony_ci uasm_i_dsrl_safe(p, scratch, tmp, PMD_SHIFT - 3); 12248c2ecf20Sopenharmony_ci uasm_i_andi(p, scratch, scratch, (PTRS_PER_PMD - 1) << 3); 12258c2ecf20Sopenharmony_ci GET_CONTEXT(p, tmp); /* get context reg */ 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_ci if (use_lwx_insns()) { 12288c2ecf20Sopenharmony_ci UASM_i_LWX(p, scratch, scratch, ptr); 12298c2ecf20Sopenharmony_ci } else { 12308c2ecf20Sopenharmony_ci uasm_i_daddu(p, ptr, ptr, scratch); /* add in pmd offset */ 12318c2ecf20Sopenharmony_ci UASM_i_LW(p, scratch, 0, ptr); 12328c2ecf20Sopenharmony_ci } 12338c2ecf20Sopenharmony_ci#endif 12348c2ecf20Sopenharmony_ci /* Adjust the context during the load latency. */ 12358c2ecf20Sopenharmony_ci build_adjust_context(p, tmp); 12368c2ecf20Sopenharmony_ci 12378c2ecf20Sopenharmony_ci#ifdef CONFIG_MIPS_HUGE_TLB_SUPPORT 12388c2ecf20Sopenharmony_ci uasm_il_bbit1(p, r, scratch, ilog2(_PAGE_HUGE), label_tlb_huge_update); 12398c2ecf20Sopenharmony_ci /* 12408c2ecf20Sopenharmony_ci * The in the LWX case we don't want to do the load in the 12418c2ecf20Sopenharmony_ci * delay slot. It cannot issue in the same cycle and may be 12428c2ecf20Sopenharmony_ci * speculative and unneeded. 12438c2ecf20Sopenharmony_ci */ 12448c2ecf20Sopenharmony_ci if (use_lwx_insns()) 12458c2ecf20Sopenharmony_ci uasm_i_nop(p); 12468c2ecf20Sopenharmony_ci#endif /* CONFIG_MIPS_HUGE_TLB_SUPPORT */ 12478c2ecf20Sopenharmony_ci 12488c2ecf20Sopenharmony_ci 12498c2ecf20Sopenharmony_ci /* build_update_entries */ 12508c2ecf20Sopenharmony_ci if (use_lwx_insns()) { 12518c2ecf20Sopenharmony_ci even = ptr; 12528c2ecf20Sopenharmony_ci odd = tmp; 12538c2ecf20Sopenharmony_ci UASM_i_LWX(p, even, scratch, tmp); 12548c2ecf20Sopenharmony_ci UASM_i_ADDIU(p, tmp, tmp, sizeof(pte_t)); 12558c2ecf20Sopenharmony_ci UASM_i_LWX(p, odd, scratch, tmp); 12568c2ecf20Sopenharmony_ci } else { 12578c2ecf20Sopenharmony_ci UASM_i_ADDU(p, ptr, scratch, tmp); /* add in offset */ 12588c2ecf20Sopenharmony_ci even = tmp; 12598c2ecf20Sopenharmony_ci odd = ptr; 12608c2ecf20Sopenharmony_ci UASM_i_LW(p, even, 0, ptr); /* get even pte */ 12618c2ecf20Sopenharmony_ci UASM_i_LW(p, odd, sizeof(pte_t), ptr); /* get odd pte */ 12628c2ecf20Sopenharmony_ci } 12638c2ecf20Sopenharmony_ci if (cpu_has_rixi) { 12648c2ecf20Sopenharmony_ci uasm_i_drotr(p, even, even, ilog2(_PAGE_GLOBAL)); 12658c2ecf20Sopenharmony_ci UASM_i_MTC0(p, even, C0_ENTRYLO0); /* load it */ 12668c2ecf20Sopenharmony_ci uasm_i_drotr(p, odd, odd, ilog2(_PAGE_GLOBAL)); 12678c2ecf20Sopenharmony_ci } else { 12688c2ecf20Sopenharmony_ci uasm_i_dsrl_safe(p, even, even, ilog2(_PAGE_GLOBAL)); 12698c2ecf20Sopenharmony_ci UASM_i_MTC0(p, even, C0_ENTRYLO0); /* load it */ 12708c2ecf20Sopenharmony_ci uasm_i_dsrl_safe(p, odd, odd, ilog2(_PAGE_GLOBAL)); 12718c2ecf20Sopenharmony_ci } 12728c2ecf20Sopenharmony_ci UASM_i_MTC0(p, odd, C0_ENTRYLO1); /* load it */ 12738c2ecf20Sopenharmony_ci 12748c2ecf20Sopenharmony_ci if (c0_scratch_reg >= 0) { 12758c2ecf20Sopenharmony_ci uasm_i_ehb(p); 12768c2ecf20Sopenharmony_ci UASM_i_MFC0(p, scratch, c0_kscratch(), c0_scratch_reg); 12778c2ecf20Sopenharmony_ci build_tlb_write_entry(p, l, r, tlb_random); 12788c2ecf20Sopenharmony_ci uasm_l_leave(l, *p); 12798c2ecf20Sopenharmony_ci rv.restore_scratch = 1; 12808c2ecf20Sopenharmony_ci } else if (PAGE_SHIFT == 14 || PAGE_SHIFT == 13) { 12818c2ecf20Sopenharmony_ci build_tlb_write_entry(p, l, r, tlb_random); 12828c2ecf20Sopenharmony_ci uasm_l_leave(l, *p); 12838c2ecf20Sopenharmony_ci UASM_i_LW(p, scratch, scratchpad_offset(0), 0); 12848c2ecf20Sopenharmony_ci } else { 12858c2ecf20Sopenharmony_ci UASM_i_LW(p, scratch, scratchpad_offset(0), 0); 12868c2ecf20Sopenharmony_ci build_tlb_write_entry(p, l, r, tlb_random); 12878c2ecf20Sopenharmony_ci uasm_l_leave(l, *p); 12888c2ecf20Sopenharmony_ci rv.restore_scratch = 1; 12898c2ecf20Sopenharmony_ci } 12908c2ecf20Sopenharmony_ci 12918c2ecf20Sopenharmony_ci uasm_i_eret(p); /* return from trap */ 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_ci return rv; 12948c2ecf20Sopenharmony_ci} 12958c2ecf20Sopenharmony_ci 12968c2ecf20Sopenharmony_ci/* 12978c2ecf20Sopenharmony_ci * For a 64-bit kernel, we are using the 64-bit XTLB refill exception 12988c2ecf20Sopenharmony_ci * because EXL == 0. If we wrap, we can also use the 32 instruction 12998c2ecf20Sopenharmony_ci * slots before the XTLB refill exception handler which belong to the 13008c2ecf20Sopenharmony_ci * unused TLB refill exception. 13018c2ecf20Sopenharmony_ci */ 13028c2ecf20Sopenharmony_ci#define MIPS64_REFILL_INSNS 32 13038c2ecf20Sopenharmony_ci 13048c2ecf20Sopenharmony_cistatic void build_r4000_tlb_refill_handler(void) 13058c2ecf20Sopenharmony_ci{ 13068c2ecf20Sopenharmony_ci u32 *p = tlb_handler; 13078c2ecf20Sopenharmony_ci struct uasm_label *l = labels; 13088c2ecf20Sopenharmony_ci struct uasm_reloc *r = relocs; 13098c2ecf20Sopenharmony_ci u32 *f; 13108c2ecf20Sopenharmony_ci unsigned int final_len; 13118c2ecf20Sopenharmony_ci struct mips_huge_tlb_info htlb_info __maybe_unused; 13128c2ecf20Sopenharmony_ci enum vmalloc64_mode vmalloc_mode __maybe_unused; 13138c2ecf20Sopenharmony_ci 13148c2ecf20Sopenharmony_ci memset(tlb_handler, 0, sizeof(tlb_handler)); 13158c2ecf20Sopenharmony_ci memset(labels, 0, sizeof(labels)); 13168c2ecf20Sopenharmony_ci memset(relocs, 0, sizeof(relocs)); 13178c2ecf20Sopenharmony_ci memset(final_handler, 0, sizeof(final_handler)); 13188c2ecf20Sopenharmony_ci 13198c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_64BIT) && (scratch_reg >= 0 || scratchpad_available()) && use_bbit_insns()) { 13208c2ecf20Sopenharmony_ci htlb_info = build_fast_tlb_refill_handler(&p, &l, &r, K0, K1, 13218c2ecf20Sopenharmony_ci scratch_reg); 13228c2ecf20Sopenharmony_ci vmalloc_mode = refill_scratch; 13238c2ecf20Sopenharmony_ci } else { 13248c2ecf20Sopenharmony_ci htlb_info.huge_pte = K0; 13258c2ecf20Sopenharmony_ci htlb_info.restore_scratch = 0; 13268c2ecf20Sopenharmony_ci htlb_info.need_reload_pte = true; 13278c2ecf20Sopenharmony_ci vmalloc_mode = refill_noscratch; 13288c2ecf20Sopenharmony_ci /* 13298c2ecf20Sopenharmony_ci * create the plain linear handler 13308c2ecf20Sopenharmony_ci */ 13318c2ecf20Sopenharmony_ci if (bcm1250_m3_war()) { 13328c2ecf20Sopenharmony_ci unsigned int segbits = 44; 13338c2ecf20Sopenharmony_ci 13348c2ecf20Sopenharmony_ci uasm_i_dmfc0(&p, K0, C0_BADVADDR); 13358c2ecf20Sopenharmony_ci uasm_i_dmfc0(&p, K1, C0_ENTRYHI); 13368c2ecf20Sopenharmony_ci uasm_i_xor(&p, K0, K0, K1); 13378c2ecf20Sopenharmony_ci uasm_i_dsrl_safe(&p, K1, K0, 62); 13388c2ecf20Sopenharmony_ci uasm_i_dsrl_safe(&p, K0, K0, 12 + 1); 13398c2ecf20Sopenharmony_ci uasm_i_dsll_safe(&p, K0, K0, 64 + 12 + 1 - segbits); 13408c2ecf20Sopenharmony_ci uasm_i_or(&p, K0, K0, K1); 13418c2ecf20Sopenharmony_ci uasm_il_bnez(&p, &r, K0, label_leave); 13428c2ecf20Sopenharmony_ci /* No need for uasm_i_nop */ 13438c2ecf20Sopenharmony_ci } 13448c2ecf20Sopenharmony_ci 13458c2ecf20Sopenharmony_ci#ifdef CONFIG_64BIT 13468c2ecf20Sopenharmony_ci build_get_pmde64(&p, &l, &r, K0, K1); /* get pmd in K1 */ 13478c2ecf20Sopenharmony_ci#else 13488c2ecf20Sopenharmony_ci build_get_pgde32(&p, K0, K1); /* get pgd in K1 */ 13498c2ecf20Sopenharmony_ci#endif 13508c2ecf20Sopenharmony_ci 13518c2ecf20Sopenharmony_ci#ifdef CONFIG_MIPS_HUGE_TLB_SUPPORT 13528c2ecf20Sopenharmony_ci build_is_huge_pte(&p, &r, K0, K1, label_tlb_huge_update); 13538c2ecf20Sopenharmony_ci#endif 13548c2ecf20Sopenharmony_ci 13558c2ecf20Sopenharmony_ci build_get_ptep(&p, K0, K1); 13568c2ecf20Sopenharmony_ci build_update_entries(&p, K0, K1); 13578c2ecf20Sopenharmony_ci build_tlb_write_entry(&p, &l, &r, tlb_random); 13588c2ecf20Sopenharmony_ci uasm_l_leave(&l, p); 13598c2ecf20Sopenharmony_ci uasm_i_eret(&p); /* return from trap */ 13608c2ecf20Sopenharmony_ci } 13618c2ecf20Sopenharmony_ci#ifdef CONFIG_MIPS_HUGE_TLB_SUPPORT 13628c2ecf20Sopenharmony_ci uasm_l_tlb_huge_update(&l, p); 13638c2ecf20Sopenharmony_ci if (htlb_info.need_reload_pte) 13648c2ecf20Sopenharmony_ci UASM_i_LW(&p, htlb_info.huge_pte, 0, K1); 13658c2ecf20Sopenharmony_ci build_huge_update_entries(&p, htlb_info.huge_pte, K1); 13668c2ecf20Sopenharmony_ci build_huge_tlb_write_entry(&p, &l, &r, K0, tlb_random, 13678c2ecf20Sopenharmony_ci htlb_info.restore_scratch); 13688c2ecf20Sopenharmony_ci#endif 13698c2ecf20Sopenharmony_ci 13708c2ecf20Sopenharmony_ci#ifdef CONFIG_64BIT 13718c2ecf20Sopenharmony_ci build_get_pgd_vmalloc64(&p, &l, &r, K0, K1, vmalloc_mode); 13728c2ecf20Sopenharmony_ci#endif 13738c2ecf20Sopenharmony_ci 13748c2ecf20Sopenharmony_ci /* 13758c2ecf20Sopenharmony_ci * Overflow check: For the 64bit handler, we need at least one 13768c2ecf20Sopenharmony_ci * free instruction slot for the wrap-around branch. In worst 13778c2ecf20Sopenharmony_ci * case, if the intended insertion point is a delay slot, we 13788c2ecf20Sopenharmony_ci * need three, with the second nop'ed and the third being 13798c2ecf20Sopenharmony_ci * unused. 13808c2ecf20Sopenharmony_ci */ 13818c2ecf20Sopenharmony_ci switch (boot_cpu_type()) { 13828c2ecf20Sopenharmony_ci default: 13838c2ecf20Sopenharmony_ci if (sizeof(long) == 4) { 13848c2ecf20Sopenharmony_ci case CPU_LOONGSON2EF: 13858c2ecf20Sopenharmony_ci /* Loongson2 ebase is different than r4k, we have more space */ 13868c2ecf20Sopenharmony_ci if ((p - tlb_handler) > 64) 13878c2ecf20Sopenharmony_ci panic("TLB refill handler space exceeded"); 13888c2ecf20Sopenharmony_ci /* 13898c2ecf20Sopenharmony_ci * Now fold the handler in the TLB refill handler space. 13908c2ecf20Sopenharmony_ci */ 13918c2ecf20Sopenharmony_ci f = final_handler; 13928c2ecf20Sopenharmony_ci /* Simplest case, just copy the handler. */ 13938c2ecf20Sopenharmony_ci uasm_copy_handler(relocs, labels, tlb_handler, p, f); 13948c2ecf20Sopenharmony_ci final_len = p - tlb_handler; 13958c2ecf20Sopenharmony_ci break; 13968c2ecf20Sopenharmony_ci } else { 13978c2ecf20Sopenharmony_ci if (((p - tlb_handler) > (MIPS64_REFILL_INSNS * 2) - 1) 13988c2ecf20Sopenharmony_ci || (((p - tlb_handler) > (MIPS64_REFILL_INSNS * 2) - 3) 13998c2ecf20Sopenharmony_ci && uasm_insn_has_bdelay(relocs, 14008c2ecf20Sopenharmony_ci tlb_handler + MIPS64_REFILL_INSNS - 3))) 14018c2ecf20Sopenharmony_ci panic("TLB refill handler space exceeded"); 14028c2ecf20Sopenharmony_ci /* 14038c2ecf20Sopenharmony_ci * Now fold the handler in the TLB refill handler space. 14048c2ecf20Sopenharmony_ci */ 14058c2ecf20Sopenharmony_ci f = final_handler + MIPS64_REFILL_INSNS; 14068c2ecf20Sopenharmony_ci if ((p - tlb_handler) <= MIPS64_REFILL_INSNS) { 14078c2ecf20Sopenharmony_ci /* Just copy the handler. */ 14088c2ecf20Sopenharmony_ci uasm_copy_handler(relocs, labels, tlb_handler, p, f); 14098c2ecf20Sopenharmony_ci final_len = p - tlb_handler; 14108c2ecf20Sopenharmony_ci } else { 14118c2ecf20Sopenharmony_ci#ifdef CONFIG_MIPS_HUGE_TLB_SUPPORT 14128c2ecf20Sopenharmony_ci const enum label_id ls = label_tlb_huge_update; 14138c2ecf20Sopenharmony_ci#else 14148c2ecf20Sopenharmony_ci const enum label_id ls = label_vmalloc; 14158c2ecf20Sopenharmony_ci#endif 14168c2ecf20Sopenharmony_ci u32 *split; 14178c2ecf20Sopenharmony_ci int ov = 0; 14188c2ecf20Sopenharmony_ci int i; 14198c2ecf20Sopenharmony_ci 14208c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(labels) && labels[i].lab != ls; i++) 14218c2ecf20Sopenharmony_ci ; 14228c2ecf20Sopenharmony_ci BUG_ON(i == ARRAY_SIZE(labels)); 14238c2ecf20Sopenharmony_ci split = labels[i].addr; 14248c2ecf20Sopenharmony_ci 14258c2ecf20Sopenharmony_ci /* 14268c2ecf20Sopenharmony_ci * See if we have overflown one way or the other. 14278c2ecf20Sopenharmony_ci */ 14288c2ecf20Sopenharmony_ci if (split > tlb_handler + MIPS64_REFILL_INSNS || 14298c2ecf20Sopenharmony_ci split < p - MIPS64_REFILL_INSNS) 14308c2ecf20Sopenharmony_ci ov = 1; 14318c2ecf20Sopenharmony_ci 14328c2ecf20Sopenharmony_ci if (ov) { 14338c2ecf20Sopenharmony_ci /* 14348c2ecf20Sopenharmony_ci * Split two instructions before the end. One 14358c2ecf20Sopenharmony_ci * for the branch and one for the instruction 14368c2ecf20Sopenharmony_ci * in the delay slot. 14378c2ecf20Sopenharmony_ci */ 14388c2ecf20Sopenharmony_ci split = tlb_handler + MIPS64_REFILL_INSNS - 2; 14398c2ecf20Sopenharmony_ci 14408c2ecf20Sopenharmony_ci /* 14418c2ecf20Sopenharmony_ci * If the branch would fall in a delay slot, 14428c2ecf20Sopenharmony_ci * we must back up an additional instruction 14438c2ecf20Sopenharmony_ci * so that it is no longer in a delay slot. 14448c2ecf20Sopenharmony_ci */ 14458c2ecf20Sopenharmony_ci if (uasm_insn_has_bdelay(relocs, split - 1)) 14468c2ecf20Sopenharmony_ci split--; 14478c2ecf20Sopenharmony_ci } 14488c2ecf20Sopenharmony_ci /* Copy first part of the handler. */ 14498c2ecf20Sopenharmony_ci uasm_copy_handler(relocs, labels, tlb_handler, split, f); 14508c2ecf20Sopenharmony_ci f += split - tlb_handler; 14518c2ecf20Sopenharmony_ci 14528c2ecf20Sopenharmony_ci if (ov) { 14538c2ecf20Sopenharmony_ci /* Insert branch. */ 14548c2ecf20Sopenharmony_ci uasm_l_split(&l, final_handler); 14558c2ecf20Sopenharmony_ci uasm_il_b(&f, &r, label_split); 14568c2ecf20Sopenharmony_ci if (uasm_insn_has_bdelay(relocs, split)) 14578c2ecf20Sopenharmony_ci uasm_i_nop(&f); 14588c2ecf20Sopenharmony_ci else { 14598c2ecf20Sopenharmony_ci uasm_copy_handler(relocs, labels, 14608c2ecf20Sopenharmony_ci split, split + 1, f); 14618c2ecf20Sopenharmony_ci uasm_move_labels(labels, f, f + 1, -1); 14628c2ecf20Sopenharmony_ci f++; 14638c2ecf20Sopenharmony_ci split++; 14648c2ecf20Sopenharmony_ci } 14658c2ecf20Sopenharmony_ci } 14668c2ecf20Sopenharmony_ci 14678c2ecf20Sopenharmony_ci /* Copy the rest of the handler. */ 14688c2ecf20Sopenharmony_ci uasm_copy_handler(relocs, labels, split, p, final_handler); 14698c2ecf20Sopenharmony_ci final_len = (f - (final_handler + MIPS64_REFILL_INSNS)) + 14708c2ecf20Sopenharmony_ci (p - split); 14718c2ecf20Sopenharmony_ci } 14728c2ecf20Sopenharmony_ci } 14738c2ecf20Sopenharmony_ci break; 14748c2ecf20Sopenharmony_ci } 14758c2ecf20Sopenharmony_ci 14768c2ecf20Sopenharmony_ci uasm_resolve_relocs(relocs, labels); 14778c2ecf20Sopenharmony_ci pr_debug("Wrote TLB refill handler (%u instructions).\n", 14788c2ecf20Sopenharmony_ci final_len); 14798c2ecf20Sopenharmony_ci 14808c2ecf20Sopenharmony_ci memcpy((void *)ebase, final_handler, 0x100); 14818c2ecf20Sopenharmony_ci local_flush_icache_range(ebase, ebase + 0x100); 14828c2ecf20Sopenharmony_ci dump_handler("r4000_tlb_refill", (u32 *)ebase, (u32 *)(ebase + 0x100)); 14838c2ecf20Sopenharmony_ci} 14848c2ecf20Sopenharmony_ci 14858c2ecf20Sopenharmony_cistatic void setup_pw(void) 14868c2ecf20Sopenharmony_ci{ 14878c2ecf20Sopenharmony_ci unsigned int pwctl; 14888c2ecf20Sopenharmony_ci unsigned long pgd_i, pgd_w; 14898c2ecf20Sopenharmony_ci#ifndef __PAGETABLE_PMD_FOLDED 14908c2ecf20Sopenharmony_ci unsigned long pmd_i, pmd_w; 14918c2ecf20Sopenharmony_ci#endif 14928c2ecf20Sopenharmony_ci unsigned long pt_i, pt_w; 14938c2ecf20Sopenharmony_ci unsigned long pte_i, pte_w; 14948c2ecf20Sopenharmony_ci#ifdef CONFIG_MIPS_HUGE_TLB_SUPPORT 14958c2ecf20Sopenharmony_ci unsigned long psn; 14968c2ecf20Sopenharmony_ci 14978c2ecf20Sopenharmony_ci psn = ilog2(_PAGE_HUGE); /* bit used to indicate huge page */ 14988c2ecf20Sopenharmony_ci#endif 14998c2ecf20Sopenharmony_ci pgd_i = PGDIR_SHIFT; /* 1st level PGD */ 15008c2ecf20Sopenharmony_ci#ifndef __PAGETABLE_PMD_FOLDED 15018c2ecf20Sopenharmony_ci pgd_w = PGDIR_SHIFT - PMD_SHIFT + PGD_ORDER; 15028c2ecf20Sopenharmony_ci 15038c2ecf20Sopenharmony_ci pmd_i = PMD_SHIFT; /* 2nd level PMD */ 15048c2ecf20Sopenharmony_ci pmd_w = PMD_SHIFT - PAGE_SHIFT; 15058c2ecf20Sopenharmony_ci#else 15068c2ecf20Sopenharmony_ci pgd_w = PGDIR_SHIFT - PAGE_SHIFT + PGD_ORDER; 15078c2ecf20Sopenharmony_ci#endif 15088c2ecf20Sopenharmony_ci 15098c2ecf20Sopenharmony_ci pt_i = PAGE_SHIFT; /* 3rd level PTE */ 15108c2ecf20Sopenharmony_ci pt_w = PAGE_SHIFT - 3; 15118c2ecf20Sopenharmony_ci 15128c2ecf20Sopenharmony_ci pte_i = ilog2(_PAGE_GLOBAL); 15138c2ecf20Sopenharmony_ci pte_w = 0; 15148c2ecf20Sopenharmony_ci pwctl = 1 << 30; /* Set PWDirExt */ 15158c2ecf20Sopenharmony_ci 15168c2ecf20Sopenharmony_ci#ifndef __PAGETABLE_PMD_FOLDED 15178c2ecf20Sopenharmony_ci write_c0_pwfield(pgd_i << 24 | pmd_i << 12 | pt_i << 6 | pte_i); 15188c2ecf20Sopenharmony_ci write_c0_pwsize(1 << 30 | pgd_w << 24 | pmd_w << 12 | pt_w << 6 | pte_w); 15198c2ecf20Sopenharmony_ci#else 15208c2ecf20Sopenharmony_ci write_c0_pwfield(pgd_i << 24 | pt_i << 6 | pte_i); 15218c2ecf20Sopenharmony_ci write_c0_pwsize(1 << 30 | pgd_w << 24 | pt_w << 6 | pte_w); 15228c2ecf20Sopenharmony_ci#endif 15238c2ecf20Sopenharmony_ci 15248c2ecf20Sopenharmony_ci#ifdef CONFIG_MIPS_HUGE_TLB_SUPPORT 15258c2ecf20Sopenharmony_ci pwctl |= (1 << 6 | psn); 15268c2ecf20Sopenharmony_ci#endif 15278c2ecf20Sopenharmony_ci write_c0_pwctl(pwctl); 15288c2ecf20Sopenharmony_ci write_c0_kpgd((long)swapper_pg_dir); 15298c2ecf20Sopenharmony_ci kscratch_used_mask |= (1 << 7); /* KScratch6 is used for KPGD */ 15308c2ecf20Sopenharmony_ci} 15318c2ecf20Sopenharmony_ci 15328c2ecf20Sopenharmony_cistatic void build_loongson3_tlb_refill_handler(void) 15338c2ecf20Sopenharmony_ci{ 15348c2ecf20Sopenharmony_ci u32 *p = tlb_handler; 15358c2ecf20Sopenharmony_ci struct uasm_label *l = labels; 15368c2ecf20Sopenharmony_ci struct uasm_reloc *r = relocs; 15378c2ecf20Sopenharmony_ci 15388c2ecf20Sopenharmony_ci memset(labels, 0, sizeof(labels)); 15398c2ecf20Sopenharmony_ci memset(relocs, 0, sizeof(relocs)); 15408c2ecf20Sopenharmony_ci memset(tlb_handler, 0, sizeof(tlb_handler)); 15418c2ecf20Sopenharmony_ci 15428c2ecf20Sopenharmony_ci if (check_for_high_segbits) { 15438c2ecf20Sopenharmony_ci uasm_i_dmfc0(&p, K0, C0_BADVADDR); 15448c2ecf20Sopenharmony_ci uasm_i_dsrl_safe(&p, K1, K0, PGDIR_SHIFT + PGD_ORDER + PAGE_SHIFT - 3); 15458c2ecf20Sopenharmony_ci uasm_il_beqz(&p, &r, K1, label_vmalloc); 15468c2ecf20Sopenharmony_ci uasm_i_nop(&p); 15478c2ecf20Sopenharmony_ci 15488c2ecf20Sopenharmony_ci uasm_il_bgez(&p, &r, K0, label_large_segbits_fault); 15498c2ecf20Sopenharmony_ci uasm_i_nop(&p); 15508c2ecf20Sopenharmony_ci uasm_l_vmalloc(&l, p); 15518c2ecf20Sopenharmony_ci } 15528c2ecf20Sopenharmony_ci 15538c2ecf20Sopenharmony_ci uasm_i_dmfc0(&p, K1, C0_PGD); 15548c2ecf20Sopenharmony_ci 15558c2ecf20Sopenharmony_ci uasm_i_lddir(&p, K0, K1, 3); /* global page dir */ 15568c2ecf20Sopenharmony_ci#ifndef __PAGETABLE_PMD_FOLDED 15578c2ecf20Sopenharmony_ci uasm_i_lddir(&p, K1, K0, 1); /* middle page dir */ 15588c2ecf20Sopenharmony_ci#endif 15598c2ecf20Sopenharmony_ci uasm_i_ldpte(&p, K1, 0); /* even */ 15608c2ecf20Sopenharmony_ci uasm_i_ldpte(&p, K1, 1); /* odd */ 15618c2ecf20Sopenharmony_ci uasm_i_tlbwr(&p); 15628c2ecf20Sopenharmony_ci 15638c2ecf20Sopenharmony_ci /* restore page mask */ 15648c2ecf20Sopenharmony_ci if (PM_DEFAULT_MASK >> 16) { 15658c2ecf20Sopenharmony_ci uasm_i_lui(&p, K0, PM_DEFAULT_MASK >> 16); 15668c2ecf20Sopenharmony_ci uasm_i_ori(&p, K0, K0, PM_DEFAULT_MASK & 0xffff); 15678c2ecf20Sopenharmony_ci uasm_i_mtc0(&p, K0, C0_PAGEMASK); 15688c2ecf20Sopenharmony_ci } else if (PM_DEFAULT_MASK) { 15698c2ecf20Sopenharmony_ci uasm_i_ori(&p, K0, 0, PM_DEFAULT_MASK); 15708c2ecf20Sopenharmony_ci uasm_i_mtc0(&p, K0, C0_PAGEMASK); 15718c2ecf20Sopenharmony_ci } else { 15728c2ecf20Sopenharmony_ci uasm_i_mtc0(&p, 0, C0_PAGEMASK); 15738c2ecf20Sopenharmony_ci } 15748c2ecf20Sopenharmony_ci 15758c2ecf20Sopenharmony_ci uasm_i_eret(&p); 15768c2ecf20Sopenharmony_ci 15778c2ecf20Sopenharmony_ci if (check_for_high_segbits) { 15788c2ecf20Sopenharmony_ci uasm_l_large_segbits_fault(&l, p); 15798c2ecf20Sopenharmony_ci UASM_i_LA(&p, K1, (unsigned long)tlb_do_page_fault_0); 15808c2ecf20Sopenharmony_ci uasm_i_jr(&p, K1); 15818c2ecf20Sopenharmony_ci uasm_i_nop(&p); 15828c2ecf20Sopenharmony_ci } 15838c2ecf20Sopenharmony_ci 15848c2ecf20Sopenharmony_ci uasm_resolve_relocs(relocs, labels); 15858c2ecf20Sopenharmony_ci memcpy((void *)(ebase + 0x80), tlb_handler, 0x80); 15868c2ecf20Sopenharmony_ci local_flush_icache_range(ebase + 0x80, ebase + 0x100); 15878c2ecf20Sopenharmony_ci dump_handler("loongson3_tlb_refill", 15888c2ecf20Sopenharmony_ci (u32 *)(ebase + 0x80), (u32 *)(ebase + 0x100)); 15898c2ecf20Sopenharmony_ci} 15908c2ecf20Sopenharmony_ci 15918c2ecf20Sopenharmony_cistatic void build_setup_pgd(void) 15928c2ecf20Sopenharmony_ci{ 15938c2ecf20Sopenharmony_ci const int a0 = 4; 15948c2ecf20Sopenharmony_ci const int __maybe_unused a1 = 5; 15958c2ecf20Sopenharmony_ci const int __maybe_unused a2 = 6; 15968c2ecf20Sopenharmony_ci u32 *p = (u32 *)msk_isa16_mode((ulong)tlbmiss_handler_setup_pgd); 15978c2ecf20Sopenharmony_ci#ifndef CONFIG_MIPS_PGD_C0_CONTEXT 15988c2ecf20Sopenharmony_ci long pgdc = (long)pgd_current; 15998c2ecf20Sopenharmony_ci#endif 16008c2ecf20Sopenharmony_ci 16018c2ecf20Sopenharmony_ci memset(p, 0, tlbmiss_handler_setup_pgd_end - (char *)p); 16028c2ecf20Sopenharmony_ci memset(labels, 0, sizeof(labels)); 16038c2ecf20Sopenharmony_ci memset(relocs, 0, sizeof(relocs)); 16048c2ecf20Sopenharmony_ci pgd_reg = allocate_kscratch(); 16058c2ecf20Sopenharmony_ci#ifdef CONFIG_MIPS_PGD_C0_CONTEXT 16068c2ecf20Sopenharmony_ci if (pgd_reg == -1) { 16078c2ecf20Sopenharmony_ci struct uasm_label *l = labels; 16088c2ecf20Sopenharmony_ci struct uasm_reloc *r = relocs; 16098c2ecf20Sopenharmony_ci 16108c2ecf20Sopenharmony_ci /* PGD << 11 in c0_Context */ 16118c2ecf20Sopenharmony_ci /* 16128c2ecf20Sopenharmony_ci * If it is a ckseg0 address, convert to a physical 16138c2ecf20Sopenharmony_ci * address. Shifting right by 29 and adding 4 will 16148c2ecf20Sopenharmony_ci * result in zero for these addresses. 16158c2ecf20Sopenharmony_ci * 16168c2ecf20Sopenharmony_ci */ 16178c2ecf20Sopenharmony_ci UASM_i_SRA(&p, a1, a0, 29); 16188c2ecf20Sopenharmony_ci UASM_i_ADDIU(&p, a1, a1, 4); 16198c2ecf20Sopenharmony_ci uasm_il_bnez(&p, &r, a1, label_tlbl_goaround1); 16208c2ecf20Sopenharmony_ci uasm_i_nop(&p); 16218c2ecf20Sopenharmony_ci uasm_i_dinsm(&p, a0, 0, 29, 64 - 29); 16228c2ecf20Sopenharmony_ci uasm_l_tlbl_goaround1(&l, p); 16238c2ecf20Sopenharmony_ci UASM_i_SLL(&p, a0, a0, 11); 16248c2ecf20Sopenharmony_ci UASM_i_MTC0(&p, a0, C0_CONTEXT); 16258c2ecf20Sopenharmony_ci uasm_i_jr(&p, 31); 16268c2ecf20Sopenharmony_ci uasm_i_ehb(&p); 16278c2ecf20Sopenharmony_ci } else { 16288c2ecf20Sopenharmony_ci /* PGD in c0_KScratch */ 16298c2ecf20Sopenharmony_ci if (cpu_has_ldpte) 16308c2ecf20Sopenharmony_ci UASM_i_MTC0(&p, a0, C0_PWBASE); 16318c2ecf20Sopenharmony_ci else 16328c2ecf20Sopenharmony_ci UASM_i_MTC0(&p, a0, c0_kscratch(), pgd_reg); 16338c2ecf20Sopenharmony_ci uasm_i_jr(&p, 31); 16348c2ecf20Sopenharmony_ci uasm_i_ehb(&p); 16358c2ecf20Sopenharmony_ci } 16368c2ecf20Sopenharmony_ci#else 16378c2ecf20Sopenharmony_ci#ifdef CONFIG_SMP 16388c2ecf20Sopenharmony_ci /* Save PGD to pgd_current[smp_processor_id()] */ 16398c2ecf20Sopenharmony_ci UASM_i_CPUID_MFC0(&p, a1, SMP_CPUID_REG); 16408c2ecf20Sopenharmony_ci UASM_i_SRL_SAFE(&p, a1, a1, SMP_CPUID_PTRSHIFT); 16418c2ecf20Sopenharmony_ci UASM_i_LA_mostly(&p, a2, pgdc); 16428c2ecf20Sopenharmony_ci UASM_i_ADDU(&p, a2, a2, a1); 16438c2ecf20Sopenharmony_ci UASM_i_SW(&p, a0, uasm_rel_lo(pgdc), a2); 16448c2ecf20Sopenharmony_ci#else 16458c2ecf20Sopenharmony_ci UASM_i_LA_mostly(&p, a2, pgdc); 16468c2ecf20Sopenharmony_ci UASM_i_SW(&p, a0, uasm_rel_lo(pgdc), a2); 16478c2ecf20Sopenharmony_ci#endif /* SMP */ 16488c2ecf20Sopenharmony_ci 16498c2ecf20Sopenharmony_ci /* if pgd_reg is allocated, save PGD also to scratch register */ 16508c2ecf20Sopenharmony_ci if (pgd_reg != -1) { 16518c2ecf20Sopenharmony_ci UASM_i_MTC0(&p, a0, c0_kscratch(), pgd_reg); 16528c2ecf20Sopenharmony_ci uasm_i_jr(&p, 31); 16538c2ecf20Sopenharmony_ci uasm_i_ehb(&p); 16548c2ecf20Sopenharmony_ci } else { 16558c2ecf20Sopenharmony_ci uasm_i_jr(&p, 31); 16568c2ecf20Sopenharmony_ci uasm_i_nop(&p); 16578c2ecf20Sopenharmony_ci } 16588c2ecf20Sopenharmony_ci#endif 16598c2ecf20Sopenharmony_ci if (p >= (u32 *)tlbmiss_handler_setup_pgd_end) 16608c2ecf20Sopenharmony_ci panic("tlbmiss_handler_setup_pgd space exceeded"); 16618c2ecf20Sopenharmony_ci 16628c2ecf20Sopenharmony_ci uasm_resolve_relocs(relocs, labels); 16638c2ecf20Sopenharmony_ci pr_debug("Wrote tlbmiss_handler_setup_pgd (%u instructions).\n", 16648c2ecf20Sopenharmony_ci (unsigned int)(p - (u32 *)tlbmiss_handler_setup_pgd)); 16658c2ecf20Sopenharmony_ci 16668c2ecf20Sopenharmony_ci dump_handler("tlbmiss_handler", tlbmiss_handler_setup_pgd, 16678c2ecf20Sopenharmony_ci tlbmiss_handler_setup_pgd_end); 16688c2ecf20Sopenharmony_ci} 16698c2ecf20Sopenharmony_ci 16708c2ecf20Sopenharmony_cistatic void 16718c2ecf20Sopenharmony_ciiPTE_LW(u32 **p, unsigned int pte, unsigned int ptr) 16728c2ecf20Sopenharmony_ci{ 16738c2ecf20Sopenharmony_ci#ifdef CONFIG_SMP 16748c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_CPU_LOONGSON3_WORKAROUNDS)) 16758c2ecf20Sopenharmony_ci uasm_i_sync(p, 0); 16768c2ecf20Sopenharmony_ci# ifdef CONFIG_PHYS_ADDR_T_64BIT 16778c2ecf20Sopenharmony_ci if (cpu_has_64bits) 16788c2ecf20Sopenharmony_ci uasm_i_lld(p, pte, 0, ptr); 16798c2ecf20Sopenharmony_ci else 16808c2ecf20Sopenharmony_ci# endif 16818c2ecf20Sopenharmony_ci UASM_i_LL(p, pte, 0, ptr); 16828c2ecf20Sopenharmony_ci#else 16838c2ecf20Sopenharmony_ci# ifdef CONFIG_PHYS_ADDR_T_64BIT 16848c2ecf20Sopenharmony_ci if (cpu_has_64bits) 16858c2ecf20Sopenharmony_ci uasm_i_ld(p, pte, 0, ptr); 16868c2ecf20Sopenharmony_ci else 16878c2ecf20Sopenharmony_ci# endif 16888c2ecf20Sopenharmony_ci UASM_i_LW(p, pte, 0, ptr); 16898c2ecf20Sopenharmony_ci#endif 16908c2ecf20Sopenharmony_ci} 16918c2ecf20Sopenharmony_ci 16928c2ecf20Sopenharmony_cistatic void 16938c2ecf20Sopenharmony_ciiPTE_SW(u32 **p, struct uasm_reloc **r, unsigned int pte, unsigned int ptr, 16948c2ecf20Sopenharmony_ci unsigned int mode, unsigned int scratch) 16958c2ecf20Sopenharmony_ci{ 16968c2ecf20Sopenharmony_ci unsigned int hwmode = mode & (_PAGE_VALID | _PAGE_DIRTY); 16978c2ecf20Sopenharmony_ci unsigned int swmode = mode & ~hwmode; 16988c2ecf20Sopenharmony_ci 16998c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_XPA) && !cpu_has_64bits) { 17008c2ecf20Sopenharmony_ci uasm_i_lui(p, scratch, swmode >> 16); 17018c2ecf20Sopenharmony_ci uasm_i_or(p, pte, pte, scratch); 17028c2ecf20Sopenharmony_ci BUG_ON(swmode & 0xffff); 17038c2ecf20Sopenharmony_ci } else { 17048c2ecf20Sopenharmony_ci uasm_i_ori(p, pte, pte, mode); 17058c2ecf20Sopenharmony_ci } 17068c2ecf20Sopenharmony_ci 17078c2ecf20Sopenharmony_ci#ifdef CONFIG_SMP 17088c2ecf20Sopenharmony_ci# ifdef CONFIG_PHYS_ADDR_T_64BIT 17098c2ecf20Sopenharmony_ci if (cpu_has_64bits) 17108c2ecf20Sopenharmony_ci uasm_i_scd(p, pte, 0, ptr); 17118c2ecf20Sopenharmony_ci else 17128c2ecf20Sopenharmony_ci# endif 17138c2ecf20Sopenharmony_ci UASM_i_SC(p, pte, 0, ptr); 17148c2ecf20Sopenharmony_ci 17158c2ecf20Sopenharmony_ci if (r10000_llsc_war()) 17168c2ecf20Sopenharmony_ci uasm_il_beqzl(p, r, pte, label_smp_pgtable_change); 17178c2ecf20Sopenharmony_ci else 17188c2ecf20Sopenharmony_ci uasm_il_beqz(p, r, pte, label_smp_pgtable_change); 17198c2ecf20Sopenharmony_ci 17208c2ecf20Sopenharmony_ci# ifdef CONFIG_PHYS_ADDR_T_64BIT 17218c2ecf20Sopenharmony_ci if (!cpu_has_64bits) { 17228c2ecf20Sopenharmony_ci /* no uasm_i_nop needed */ 17238c2ecf20Sopenharmony_ci uasm_i_ll(p, pte, sizeof(pte_t) / 2, ptr); 17248c2ecf20Sopenharmony_ci uasm_i_ori(p, pte, pte, hwmode); 17258c2ecf20Sopenharmony_ci BUG_ON(hwmode & ~0xffff); 17268c2ecf20Sopenharmony_ci uasm_i_sc(p, pte, sizeof(pte_t) / 2, ptr); 17278c2ecf20Sopenharmony_ci uasm_il_beqz(p, r, pte, label_smp_pgtable_change); 17288c2ecf20Sopenharmony_ci /* no uasm_i_nop needed */ 17298c2ecf20Sopenharmony_ci uasm_i_lw(p, pte, 0, ptr); 17308c2ecf20Sopenharmony_ci } else 17318c2ecf20Sopenharmony_ci uasm_i_nop(p); 17328c2ecf20Sopenharmony_ci# else 17338c2ecf20Sopenharmony_ci uasm_i_nop(p); 17348c2ecf20Sopenharmony_ci# endif 17358c2ecf20Sopenharmony_ci#else 17368c2ecf20Sopenharmony_ci# ifdef CONFIG_PHYS_ADDR_T_64BIT 17378c2ecf20Sopenharmony_ci if (cpu_has_64bits) 17388c2ecf20Sopenharmony_ci uasm_i_sd(p, pte, 0, ptr); 17398c2ecf20Sopenharmony_ci else 17408c2ecf20Sopenharmony_ci# endif 17418c2ecf20Sopenharmony_ci UASM_i_SW(p, pte, 0, ptr); 17428c2ecf20Sopenharmony_ci 17438c2ecf20Sopenharmony_ci# ifdef CONFIG_PHYS_ADDR_T_64BIT 17448c2ecf20Sopenharmony_ci if (!cpu_has_64bits) { 17458c2ecf20Sopenharmony_ci uasm_i_lw(p, pte, sizeof(pte_t) / 2, ptr); 17468c2ecf20Sopenharmony_ci uasm_i_ori(p, pte, pte, hwmode); 17478c2ecf20Sopenharmony_ci BUG_ON(hwmode & ~0xffff); 17488c2ecf20Sopenharmony_ci uasm_i_sw(p, pte, sizeof(pte_t) / 2, ptr); 17498c2ecf20Sopenharmony_ci uasm_i_lw(p, pte, 0, ptr); 17508c2ecf20Sopenharmony_ci } 17518c2ecf20Sopenharmony_ci# endif 17528c2ecf20Sopenharmony_ci#endif 17538c2ecf20Sopenharmony_ci} 17548c2ecf20Sopenharmony_ci 17558c2ecf20Sopenharmony_ci/* 17568c2ecf20Sopenharmony_ci * Check if PTE is present, if not then jump to LABEL. PTR points to 17578c2ecf20Sopenharmony_ci * the page table where this PTE is located, PTE will be re-loaded 17588c2ecf20Sopenharmony_ci * with it's original value. 17598c2ecf20Sopenharmony_ci */ 17608c2ecf20Sopenharmony_cistatic void 17618c2ecf20Sopenharmony_cibuild_pte_present(u32 **p, struct uasm_reloc **r, 17628c2ecf20Sopenharmony_ci int pte, int ptr, int scratch, enum label_id lid) 17638c2ecf20Sopenharmony_ci{ 17648c2ecf20Sopenharmony_ci int t = scratch >= 0 ? scratch : pte; 17658c2ecf20Sopenharmony_ci int cur = pte; 17668c2ecf20Sopenharmony_ci 17678c2ecf20Sopenharmony_ci if (cpu_has_rixi) { 17688c2ecf20Sopenharmony_ci if (use_bbit_insns()) { 17698c2ecf20Sopenharmony_ci uasm_il_bbit0(p, r, pte, ilog2(_PAGE_PRESENT), lid); 17708c2ecf20Sopenharmony_ci uasm_i_nop(p); 17718c2ecf20Sopenharmony_ci } else { 17728c2ecf20Sopenharmony_ci if (_PAGE_PRESENT_SHIFT) { 17738c2ecf20Sopenharmony_ci uasm_i_srl(p, t, cur, _PAGE_PRESENT_SHIFT); 17748c2ecf20Sopenharmony_ci cur = t; 17758c2ecf20Sopenharmony_ci } 17768c2ecf20Sopenharmony_ci uasm_i_andi(p, t, cur, 1); 17778c2ecf20Sopenharmony_ci uasm_il_beqz(p, r, t, lid); 17788c2ecf20Sopenharmony_ci if (pte == t) 17798c2ecf20Sopenharmony_ci /* You lose the SMP race :-(*/ 17808c2ecf20Sopenharmony_ci iPTE_LW(p, pte, ptr); 17818c2ecf20Sopenharmony_ci } 17828c2ecf20Sopenharmony_ci } else { 17838c2ecf20Sopenharmony_ci if (_PAGE_PRESENT_SHIFT) { 17848c2ecf20Sopenharmony_ci uasm_i_srl(p, t, cur, _PAGE_PRESENT_SHIFT); 17858c2ecf20Sopenharmony_ci cur = t; 17868c2ecf20Sopenharmony_ci } 17878c2ecf20Sopenharmony_ci uasm_i_andi(p, t, cur, 17888c2ecf20Sopenharmony_ci (_PAGE_PRESENT | _PAGE_NO_READ) >> _PAGE_PRESENT_SHIFT); 17898c2ecf20Sopenharmony_ci uasm_i_xori(p, t, t, _PAGE_PRESENT >> _PAGE_PRESENT_SHIFT); 17908c2ecf20Sopenharmony_ci uasm_il_bnez(p, r, t, lid); 17918c2ecf20Sopenharmony_ci if (pte == t) 17928c2ecf20Sopenharmony_ci /* You lose the SMP race :-(*/ 17938c2ecf20Sopenharmony_ci iPTE_LW(p, pte, ptr); 17948c2ecf20Sopenharmony_ci } 17958c2ecf20Sopenharmony_ci} 17968c2ecf20Sopenharmony_ci 17978c2ecf20Sopenharmony_ci/* Make PTE valid, store result in PTR. */ 17988c2ecf20Sopenharmony_cistatic void 17998c2ecf20Sopenharmony_cibuild_make_valid(u32 **p, struct uasm_reloc **r, unsigned int pte, 18008c2ecf20Sopenharmony_ci unsigned int ptr, unsigned int scratch) 18018c2ecf20Sopenharmony_ci{ 18028c2ecf20Sopenharmony_ci unsigned int mode = _PAGE_VALID | _PAGE_ACCESSED; 18038c2ecf20Sopenharmony_ci 18048c2ecf20Sopenharmony_ci iPTE_SW(p, r, pte, ptr, mode, scratch); 18058c2ecf20Sopenharmony_ci} 18068c2ecf20Sopenharmony_ci 18078c2ecf20Sopenharmony_ci/* 18088c2ecf20Sopenharmony_ci * Check if PTE can be written to, if not branch to LABEL. Regardless 18098c2ecf20Sopenharmony_ci * restore PTE with value from PTR when done. 18108c2ecf20Sopenharmony_ci */ 18118c2ecf20Sopenharmony_cistatic void 18128c2ecf20Sopenharmony_cibuild_pte_writable(u32 **p, struct uasm_reloc **r, 18138c2ecf20Sopenharmony_ci unsigned int pte, unsigned int ptr, int scratch, 18148c2ecf20Sopenharmony_ci enum label_id lid) 18158c2ecf20Sopenharmony_ci{ 18168c2ecf20Sopenharmony_ci int t = scratch >= 0 ? scratch : pte; 18178c2ecf20Sopenharmony_ci int cur = pte; 18188c2ecf20Sopenharmony_ci 18198c2ecf20Sopenharmony_ci if (_PAGE_PRESENT_SHIFT) { 18208c2ecf20Sopenharmony_ci uasm_i_srl(p, t, cur, _PAGE_PRESENT_SHIFT); 18218c2ecf20Sopenharmony_ci cur = t; 18228c2ecf20Sopenharmony_ci } 18238c2ecf20Sopenharmony_ci uasm_i_andi(p, t, cur, 18248c2ecf20Sopenharmony_ci (_PAGE_PRESENT | _PAGE_WRITE) >> _PAGE_PRESENT_SHIFT); 18258c2ecf20Sopenharmony_ci uasm_i_xori(p, t, t, 18268c2ecf20Sopenharmony_ci (_PAGE_PRESENT | _PAGE_WRITE) >> _PAGE_PRESENT_SHIFT); 18278c2ecf20Sopenharmony_ci uasm_il_bnez(p, r, t, lid); 18288c2ecf20Sopenharmony_ci if (pte == t) 18298c2ecf20Sopenharmony_ci /* You lose the SMP race :-(*/ 18308c2ecf20Sopenharmony_ci iPTE_LW(p, pte, ptr); 18318c2ecf20Sopenharmony_ci else 18328c2ecf20Sopenharmony_ci uasm_i_nop(p); 18338c2ecf20Sopenharmony_ci} 18348c2ecf20Sopenharmony_ci 18358c2ecf20Sopenharmony_ci/* Make PTE writable, update software status bits as well, then store 18368c2ecf20Sopenharmony_ci * at PTR. 18378c2ecf20Sopenharmony_ci */ 18388c2ecf20Sopenharmony_cistatic void 18398c2ecf20Sopenharmony_cibuild_make_write(u32 **p, struct uasm_reloc **r, unsigned int pte, 18408c2ecf20Sopenharmony_ci unsigned int ptr, unsigned int scratch) 18418c2ecf20Sopenharmony_ci{ 18428c2ecf20Sopenharmony_ci unsigned int mode = (_PAGE_ACCESSED | _PAGE_MODIFIED | _PAGE_VALID 18438c2ecf20Sopenharmony_ci | _PAGE_DIRTY); 18448c2ecf20Sopenharmony_ci 18458c2ecf20Sopenharmony_ci iPTE_SW(p, r, pte, ptr, mode, scratch); 18468c2ecf20Sopenharmony_ci} 18478c2ecf20Sopenharmony_ci 18488c2ecf20Sopenharmony_ci/* 18498c2ecf20Sopenharmony_ci * Check if PTE can be modified, if not branch to LABEL. Regardless 18508c2ecf20Sopenharmony_ci * restore PTE with value from PTR when done. 18518c2ecf20Sopenharmony_ci */ 18528c2ecf20Sopenharmony_cistatic void 18538c2ecf20Sopenharmony_cibuild_pte_modifiable(u32 **p, struct uasm_reloc **r, 18548c2ecf20Sopenharmony_ci unsigned int pte, unsigned int ptr, int scratch, 18558c2ecf20Sopenharmony_ci enum label_id lid) 18568c2ecf20Sopenharmony_ci{ 18578c2ecf20Sopenharmony_ci if (use_bbit_insns()) { 18588c2ecf20Sopenharmony_ci uasm_il_bbit0(p, r, pte, ilog2(_PAGE_WRITE), lid); 18598c2ecf20Sopenharmony_ci uasm_i_nop(p); 18608c2ecf20Sopenharmony_ci } else { 18618c2ecf20Sopenharmony_ci int t = scratch >= 0 ? scratch : pte; 18628c2ecf20Sopenharmony_ci uasm_i_srl(p, t, pte, _PAGE_WRITE_SHIFT); 18638c2ecf20Sopenharmony_ci uasm_i_andi(p, t, t, 1); 18648c2ecf20Sopenharmony_ci uasm_il_beqz(p, r, t, lid); 18658c2ecf20Sopenharmony_ci if (pte == t) 18668c2ecf20Sopenharmony_ci /* You lose the SMP race :-(*/ 18678c2ecf20Sopenharmony_ci iPTE_LW(p, pte, ptr); 18688c2ecf20Sopenharmony_ci } 18698c2ecf20Sopenharmony_ci} 18708c2ecf20Sopenharmony_ci 18718c2ecf20Sopenharmony_ci#ifndef CONFIG_MIPS_PGD_C0_CONTEXT 18728c2ecf20Sopenharmony_ci 18738c2ecf20Sopenharmony_ci 18748c2ecf20Sopenharmony_ci/* 18758c2ecf20Sopenharmony_ci * R3000 style TLB load/store/modify handlers. 18768c2ecf20Sopenharmony_ci */ 18778c2ecf20Sopenharmony_ci 18788c2ecf20Sopenharmony_ci/* 18798c2ecf20Sopenharmony_ci * This places the pte into ENTRYLO0 and writes it with tlbwi. 18808c2ecf20Sopenharmony_ci * Then it returns. 18818c2ecf20Sopenharmony_ci */ 18828c2ecf20Sopenharmony_cistatic void 18838c2ecf20Sopenharmony_cibuild_r3000_pte_reload_tlbwi(u32 **p, unsigned int pte, unsigned int tmp) 18848c2ecf20Sopenharmony_ci{ 18858c2ecf20Sopenharmony_ci uasm_i_mtc0(p, pte, C0_ENTRYLO0); /* cp0 delay */ 18868c2ecf20Sopenharmony_ci uasm_i_mfc0(p, tmp, C0_EPC); /* cp0 delay */ 18878c2ecf20Sopenharmony_ci uasm_i_tlbwi(p); 18888c2ecf20Sopenharmony_ci uasm_i_jr(p, tmp); 18898c2ecf20Sopenharmony_ci uasm_i_rfe(p); /* branch delay */ 18908c2ecf20Sopenharmony_ci} 18918c2ecf20Sopenharmony_ci 18928c2ecf20Sopenharmony_ci/* 18938c2ecf20Sopenharmony_ci * This places the pte into ENTRYLO0 and writes it with tlbwi 18948c2ecf20Sopenharmony_ci * or tlbwr as appropriate. This is because the index register 18958c2ecf20Sopenharmony_ci * may have the probe fail bit set as a result of a trap on a 18968c2ecf20Sopenharmony_ci * kseg2 access, i.e. without refill. Then it returns. 18978c2ecf20Sopenharmony_ci */ 18988c2ecf20Sopenharmony_cistatic void 18998c2ecf20Sopenharmony_cibuild_r3000_tlb_reload_write(u32 **p, struct uasm_label **l, 19008c2ecf20Sopenharmony_ci struct uasm_reloc **r, unsigned int pte, 19018c2ecf20Sopenharmony_ci unsigned int tmp) 19028c2ecf20Sopenharmony_ci{ 19038c2ecf20Sopenharmony_ci uasm_i_mfc0(p, tmp, C0_INDEX); 19048c2ecf20Sopenharmony_ci uasm_i_mtc0(p, pte, C0_ENTRYLO0); /* cp0 delay */ 19058c2ecf20Sopenharmony_ci uasm_il_bltz(p, r, tmp, label_r3000_write_probe_fail); /* cp0 delay */ 19068c2ecf20Sopenharmony_ci uasm_i_mfc0(p, tmp, C0_EPC); /* branch delay */ 19078c2ecf20Sopenharmony_ci uasm_i_tlbwi(p); /* cp0 delay */ 19088c2ecf20Sopenharmony_ci uasm_i_jr(p, tmp); 19098c2ecf20Sopenharmony_ci uasm_i_rfe(p); /* branch delay */ 19108c2ecf20Sopenharmony_ci uasm_l_r3000_write_probe_fail(l, *p); 19118c2ecf20Sopenharmony_ci uasm_i_tlbwr(p); /* cp0 delay */ 19128c2ecf20Sopenharmony_ci uasm_i_jr(p, tmp); 19138c2ecf20Sopenharmony_ci uasm_i_rfe(p); /* branch delay */ 19148c2ecf20Sopenharmony_ci} 19158c2ecf20Sopenharmony_ci 19168c2ecf20Sopenharmony_cistatic void 19178c2ecf20Sopenharmony_cibuild_r3000_tlbchange_handler_head(u32 **p, unsigned int pte, 19188c2ecf20Sopenharmony_ci unsigned int ptr) 19198c2ecf20Sopenharmony_ci{ 19208c2ecf20Sopenharmony_ci long pgdc = (long)pgd_current; 19218c2ecf20Sopenharmony_ci 19228c2ecf20Sopenharmony_ci uasm_i_mfc0(p, pte, C0_BADVADDR); 19238c2ecf20Sopenharmony_ci uasm_i_lui(p, ptr, uasm_rel_hi(pgdc)); /* cp0 delay */ 19248c2ecf20Sopenharmony_ci uasm_i_lw(p, ptr, uasm_rel_lo(pgdc), ptr); 19258c2ecf20Sopenharmony_ci uasm_i_srl(p, pte, pte, 22); /* load delay */ 19268c2ecf20Sopenharmony_ci uasm_i_sll(p, pte, pte, 2); 19278c2ecf20Sopenharmony_ci uasm_i_addu(p, ptr, ptr, pte); 19288c2ecf20Sopenharmony_ci uasm_i_mfc0(p, pte, C0_CONTEXT); 19298c2ecf20Sopenharmony_ci uasm_i_lw(p, ptr, 0, ptr); /* cp0 delay */ 19308c2ecf20Sopenharmony_ci uasm_i_andi(p, pte, pte, 0xffc); /* load delay */ 19318c2ecf20Sopenharmony_ci uasm_i_addu(p, ptr, ptr, pte); 19328c2ecf20Sopenharmony_ci uasm_i_lw(p, pte, 0, ptr); 19338c2ecf20Sopenharmony_ci uasm_i_tlbp(p); /* load delay */ 19348c2ecf20Sopenharmony_ci} 19358c2ecf20Sopenharmony_ci 19368c2ecf20Sopenharmony_cistatic void build_r3000_tlb_load_handler(void) 19378c2ecf20Sopenharmony_ci{ 19388c2ecf20Sopenharmony_ci u32 *p = (u32 *)handle_tlbl; 19398c2ecf20Sopenharmony_ci struct uasm_label *l = labels; 19408c2ecf20Sopenharmony_ci struct uasm_reloc *r = relocs; 19418c2ecf20Sopenharmony_ci 19428c2ecf20Sopenharmony_ci memset(p, 0, handle_tlbl_end - (char *)p); 19438c2ecf20Sopenharmony_ci memset(labels, 0, sizeof(labels)); 19448c2ecf20Sopenharmony_ci memset(relocs, 0, sizeof(relocs)); 19458c2ecf20Sopenharmony_ci 19468c2ecf20Sopenharmony_ci build_r3000_tlbchange_handler_head(&p, K0, K1); 19478c2ecf20Sopenharmony_ci build_pte_present(&p, &r, K0, K1, -1, label_nopage_tlbl); 19488c2ecf20Sopenharmony_ci uasm_i_nop(&p); /* load delay */ 19498c2ecf20Sopenharmony_ci build_make_valid(&p, &r, K0, K1, -1); 19508c2ecf20Sopenharmony_ci build_r3000_tlb_reload_write(&p, &l, &r, K0, K1); 19518c2ecf20Sopenharmony_ci 19528c2ecf20Sopenharmony_ci uasm_l_nopage_tlbl(&l, p); 19538c2ecf20Sopenharmony_ci uasm_i_j(&p, (unsigned long)tlb_do_page_fault_0 & 0x0fffffff); 19548c2ecf20Sopenharmony_ci uasm_i_nop(&p); 19558c2ecf20Sopenharmony_ci 19568c2ecf20Sopenharmony_ci if (p >= (u32 *)handle_tlbl_end) 19578c2ecf20Sopenharmony_ci panic("TLB load handler fastpath space exceeded"); 19588c2ecf20Sopenharmony_ci 19598c2ecf20Sopenharmony_ci uasm_resolve_relocs(relocs, labels); 19608c2ecf20Sopenharmony_ci pr_debug("Wrote TLB load handler fastpath (%u instructions).\n", 19618c2ecf20Sopenharmony_ci (unsigned int)(p - (u32 *)handle_tlbl)); 19628c2ecf20Sopenharmony_ci 19638c2ecf20Sopenharmony_ci dump_handler("r3000_tlb_load", handle_tlbl, handle_tlbl_end); 19648c2ecf20Sopenharmony_ci} 19658c2ecf20Sopenharmony_ci 19668c2ecf20Sopenharmony_cistatic void build_r3000_tlb_store_handler(void) 19678c2ecf20Sopenharmony_ci{ 19688c2ecf20Sopenharmony_ci u32 *p = (u32 *)handle_tlbs; 19698c2ecf20Sopenharmony_ci struct uasm_label *l = labels; 19708c2ecf20Sopenharmony_ci struct uasm_reloc *r = relocs; 19718c2ecf20Sopenharmony_ci 19728c2ecf20Sopenharmony_ci memset(p, 0, handle_tlbs_end - (char *)p); 19738c2ecf20Sopenharmony_ci memset(labels, 0, sizeof(labels)); 19748c2ecf20Sopenharmony_ci memset(relocs, 0, sizeof(relocs)); 19758c2ecf20Sopenharmony_ci 19768c2ecf20Sopenharmony_ci build_r3000_tlbchange_handler_head(&p, K0, K1); 19778c2ecf20Sopenharmony_ci build_pte_writable(&p, &r, K0, K1, -1, label_nopage_tlbs); 19788c2ecf20Sopenharmony_ci uasm_i_nop(&p); /* load delay */ 19798c2ecf20Sopenharmony_ci build_make_write(&p, &r, K0, K1, -1); 19808c2ecf20Sopenharmony_ci build_r3000_tlb_reload_write(&p, &l, &r, K0, K1); 19818c2ecf20Sopenharmony_ci 19828c2ecf20Sopenharmony_ci uasm_l_nopage_tlbs(&l, p); 19838c2ecf20Sopenharmony_ci uasm_i_j(&p, (unsigned long)tlb_do_page_fault_1 & 0x0fffffff); 19848c2ecf20Sopenharmony_ci uasm_i_nop(&p); 19858c2ecf20Sopenharmony_ci 19868c2ecf20Sopenharmony_ci if (p >= (u32 *)handle_tlbs_end) 19878c2ecf20Sopenharmony_ci panic("TLB store handler fastpath space exceeded"); 19888c2ecf20Sopenharmony_ci 19898c2ecf20Sopenharmony_ci uasm_resolve_relocs(relocs, labels); 19908c2ecf20Sopenharmony_ci pr_debug("Wrote TLB store handler fastpath (%u instructions).\n", 19918c2ecf20Sopenharmony_ci (unsigned int)(p - (u32 *)handle_tlbs)); 19928c2ecf20Sopenharmony_ci 19938c2ecf20Sopenharmony_ci dump_handler("r3000_tlb_store", handle_tlbs, handle_tlbs_end); 19948c2ecf20Sopenharmony_ci} 19958c2ecf20Sopenharmony_ci 19968c2ecf20Sopenharmony_cistatic void build_r3000_tlb_modify_handler(void) 19978c2ecf20Sopenharmony_ci{ 19988c2ecf20Sopenharmony_ci u32 *p = (u32 *)handle_tlbm; 19998c2ecf20Sopenharmony_ci struct uasm_label *l = labels; 20008c2ecf20Sopenharmony_ci struct uasm_reloc *r = relocs; 20018c2ecf20Sopenharmony_ci 20028c2ecf20Sopenharmony_ci memset(p, 0, handle_tlbm_end - (char *)p); 20038c2ecf20Sopenharmony_ci memset(labels, 0, sizeof(labels)); 20048c2ecf20Sopenharmony_ci memset(relocs, 0, sizeof(relocs)); 20058c2ecf20Sopenharmony_ci 20068c2ecf20Sopenharmony_ci build_r3000_tlbchange_handler_head(&p, K0, K1); 20078c2ecf20Sopenharmony_ci build_pte_modifiable(&p, &r, K0, K1, -1, label_nopage_tlbm); 20088c2ecf20Sopenharmony_ci uasm_i_nop(&p); /* load delay */ 20098c2ecf20Sopenharmony_ci build_make_write(&p, &r, K0, K1, -1); 20108c2ecf20Sopenharmony_ci build_r3000_pte_reload_tlbwi(&p, K0, K1); 20118c2ecf20Sopenharmony_ci 20128c2ecf20Sopenharmony_ci uasm_l_nopage_tlbm(&l, p); 20138c2ecf20Sopenharmony_ci uasm_i_j(&p, (unsigned long)tlb_do_page_fault_1 & 0x0fffffff); 20148c2ecf20Sopenharmony_ci uasm_i_nop(&p); 20158c2ecf20Sopenharmony_ci 20168c2ecf20Sopenharmony_ci if (p >= (u32 *)handle_tlbm_end) 20178c2ecf20Sopenharmony_ci panic("TLB modify handler fastpath space exceeded"); 20188c2ecf20Sopenharmony_ci 20198c2ecf20Sopenharmony_ci uasm_resolve_relocs(relocs, labels); 20208c2ecf20Sopenharmony_ci pr_debug("Wrote TLB modify handler fastpath (%u instructions).\n", 20218c2ecf20Sopenharmony_ci (unsigned int)(p - (u32 *)handle_tlbm)); 20228c2ecf20Sopenharmony_ci 20238c2ecf20Sopenharmony_ci dump_handler("r3000_tlb_modify", handle_tlbm, handle_tlbm_end); 20248c2ecf20Sopenharmony_ci} 20258c2ecf20Sopenharmony_ci#endif /* CONFIG_MIPS_PGD_C0_CONTEXT */ 20268c2ecf20Sopenharmony_ci 20278c2ecf20Sopenharmony_cistatic bool cpu_has_tlbex_tlbp_race(void) 20288c2ecf20Sopenharmony_ci{ 20298c2ecf20Sopenharmony_ci /* 20308c2ecf20Sopenharmony_ci * When a Hardware Table Walker is running it can replace TLB entries 20318c2ecf20Sopenharmony_ci * at any time, leading to a race between it & the CPU. 20328c2ecf20Sopenharmony_ci */ 20338c2ecf20Sopenharmony_ci if (cpu_has_htw) 20348c2ecf20Sopenharmony_ci return true; 20358c2ecf20Sopenharmony_ci 20368c2ecf20Sopenharmony_ci /* 20378c2ecf20Sopenharmony_ci * If the CPU shares FTLB RAM with its siblings then our entry may be 20388c2ecf20Sopenharmony_ci * replaced at any time by a sibling performing a write to the FTLB. 20398c2ecf20Sopenharmony_ci */ 20408c2ecf20Sopenharmony_ci if (cpu_has_shared_ftlb_ram) 20418c2ecf20Sopenharmony_ci return true; 20428c2ecf20Sopenharmony_ci 20438c2ecf20Sopenharmony_ci /* In all other cases there ought to be no race condition to handle */ 20448c2ecf20Sopenharmony_ci return false; 20458c2ecf20Sopenharmony_ci} 20468c2ecf20Sopenharmony_ci 20478c2ecf20Sopenharmony_ci/* 20488c2ecf20Sopenharmony_ci * R4000 style TLB load/store/modify handlers. 20498c2ecf20Sopenharmony_ci */ 20508c2ecf20Sopenharmony_cistatic struct work_registers 20518c2ecf20Sopenharmony_cibuild_r4000_tlbchange_handler_head(u32 **p, struct uasm_label **l, 20528c2ecf20Sopenharmony_ci struct uasm_reloc **r) 20538c2ecf20Sopenharmony_ci{ 20548c2ecf20Sopenharmony_ci struct work_registers wr = build_get_work_registers(p); 20558c2ecf20Sopenharmony_ci 20568c2ecf20Sopenharmony_ci#ifdef CONFIG_64BIT 20578c2ecf20Sopenharmony_ci build_get_pmde64(p, l, r, wr.r1, wr.r2); /* get pmd in ptr */ 20588c2ecf20Sopenharmony_ci#else 20598c2ecf20Sopenharmony_ci build_get_pgde32(p, wr.r1, wr.r2); /* get pgd in ptr */ 20608c2ecf20Sopenharmony_ci#endif 20618c2ecf20Sopenharmony_ci 20628c2ecf20Sopenharmony_ci#ifdef CONFIG_MIPS_HUGE_TLB_SUPPORT 20638c2ecf20Sopenharmony_ci /* 20648c2ecf20Sopenharmony_ci * For huge tlb entries, pmd doesn't contain an address but 20658c2ecf20Sopenharmony_ci * instead contains the tlb pte. Check the PAGE_HUGE bit and 20668c2ecf20Sopenharmony_ci * see if we need to jump to huge tlb processing. 20678c2ecf20Sopenharmony_ci */ 20688c2ecf20Sopenharmony_ci build_is_huge_pte(p, r, wr.r1, wr.r2, label_tlb_huge_update); 20698c2ecf20Sopenharmony_ci#endif 20708c2ecf20Sopenharmony_ci 20718c2ecf20Sopenharmony_ci UASM_i_MFC0(p, wr.r1, C0_BADVADDR); 20728c2ecf20Sopenharmony_ci UASM_i_LW(p, wr.r2, 0, wr.r2); 20738c2ecf20Sopenharmony_ci UASM_i_SRL(p, wr.r1, wr.r1, PAGE_SHIFT + PTE_ORDER - PTE_T_LOG2); 20748c2ecf20Sopenharmony_ci uasm_i_andi(p, wr.r1, wr.r1, (PTRS_PER_PTE - 1) << PTE_T_LOG2); 20758c2ecf20Sopenharmony_ci UASM_i_ADDU(p, wr.r2, wr.r2, wr.r1); 20768c2ecf20Sopenharmony_ci 20778c2ecf20Sopenharmony_ci#ifdef CONFIG_SMP 20788c2ecf20Sopenharmony_ci uasm_l_smp_pgtable_change(l, *p); 20798c2ecf20Sopenharmony_ci#endif 20808c2ecf20Sopenharmony_ci iPTE_LW(p, wr.r1, wr.r2); /* get even pte */ 20818c2ecf20Sopenharmony_ci if (!m4kc_tlbp_war()) { 20828c2ecf20Sopenharmony_ci build_tlb_probe_entry(p); 20838c2ecf20Sopenharmony_ci if (cpu_has_tlbex_tlbp_race()) { 20848c2ecf20Sopenharmony_ci /* race condition happens, leaving */ 20858c2ecf20Sopenharmony_ci uasm_i_ehb(p); 20868c2ecf20Sopenharmony_ci uasm_i_mfc0(p, wr.r3, C0_INDEX); 20878c2ecf20Sopenharmony_ci uasm_il_bltz(p, r, wr.r3, label_leave); 20888c2ecf20Sopenharmony_ci uasm_i_nop(p); 20898c2ecf20Sopenharmony_ci } 20908c2ecf20Sopenharmony_ci } 20918c2ecf20Sopenharmony_ci return wr; 20928c2ecf20Sopenharmony_ci} 20938c2ecf20Sopenharmony_ci 20948c2ecf20Sopenharmony_cistatic void 20958c2ecf20Sopenharmony_cibuild_r4000_tlbchange_handler_tail(u32 **p, struct uasm_label **l, 20968c2ecf20Sopenharmony_ci struct uasm_reloc **r, unsigned int tmp, 20978c2ecf20Sopenharmony_ci unsigned int ptr) 20988c2ecf20Sopenharmony_ci{ 20998c2ecf20Sopenharmony_ci uasm_i_ori(p, ptr, ptr, sizeof(pte_t)); 21008c2ecf20Sopenharmony_ci uasm_i_xori(p, ptr, ptr, sizeof(pte_t)); 21018c2ecf20Sopenharmony_ci build_update_entries(p, tmp, ptr); 21028c2ecf20Sopenharmony_ci build_tlb_write_entry(p, l, r, tlb_indexed); 21038c2ecf20Sopenharmony_ci uasm_l_leave(l, *p); 21048c2ecf20Sopenharmony_ci build_restore_work_registers(p); 21058c2ecf20Sopenharmony_ci uasm_i_eret(p); /* return from trap */ 21068c2ecf20Sopenharmony_ci 21078c2ecf20Sopenharmony_ci#ifdef CONFIG_64BIT 21088c2ecf20Sopenharmony_ci build_get_pgd_vmalloc64(p, l, r, tmp, ptr, not_refill); 21098c2ecf20Sopenharmony_ci#endif 21108c2ecf20Sopenharmony_ci} 21118c2ecf20Sopenharmony_ci 21128c2ecf20Sopenharmony_cistatic void build_r4000_tlb_load_handler(void) 21138c2ecf20Sopenharmony_ci{ 21148c2ecf20Sopenharmony_ci u32 *p = (u32 *)msk_isa16_mode((ulong)handle_tlbl); 21158c2ecf20Sopenharmony_ci struct uasm_label *l = labels; 21168c2ecf20Sopenharmony_ci struct uasm_reloc *r = relocs; 21178c2ecf20Sopenharmony_ci struct work_registers wr; 21188c2ecf20Sopenharmony_ci 21198c2ecf20Sopenharmony_ci memset(p, 0, handle_tlbl_end - (char *)p); 21208c2ecf20Sopenharmony_ci memset(labels, 0, sizeof(labels)); 21218c2ecf20Sopenharmony_ci memset(relocs, 0, sizeof(relocs)); 21228c2ecf20Sopenharmony_ci 21238c2ecf20Sopenharmony_ci if (bcm1250_m3_war()) { 21248c2ecf20Sopenharmony_ci unsigned int segbits = 44; 21258c2ecf20Sopenharmony_ci 21268c2ecf20Sopenharmony_ci uasm_i_dmfc0(&p, K0, C0_BADVADDR); 21278c2ecf20Sopenharmony_ci uasm_i_dmfc0(&p, K1, C0_ENTRYHI); 21288c2ecf20Sopenharmony_ci uasm_i_xor(&p, K0, K0, K1); 21298c2ecf20Sopenharmony_ci uasm_i_dsrl_safe(&p, K1, K0, 62); 21308c2ecf20Sopenharmony_ci uasm_i_dsrl_safe(&p, K0, K0, 12 + 1); 21318c2ecf20Sopenharmony_ci uasm_i_dsll_safe(&p, K0, K0, 64 + 12 + 1 - segbits); 21328c2ecf20Sopenharmony_ci uasm_i_or(&p, K0, K0, K1); 21338c2ecf20Sopenharmony_ci uasm_il_bnez(&p, &r, K0, label_leave); 21348c2ecf20Sopenharmony_ci /* No need for uasm_i_nop */ 21358c2ecf20Sopenharmony_ci } 21368c2ecf20Sopenharmony_ci 21378c2ecf20Sopenharmony_ci wr = build_r4000_tlbchange_handler_head(&p, &l, &r); 21388c2ecf20Sopenharmony_ci build_pte_present(&p, &r, wr.r1, wr.r2, wr.r3, label_nopage_tlbl); 21398c2ecf20Sopenharmony_ci if (m4kc_tlbp_war()) 21408c2ecf20Sopenharmony_ci build_tlb_probe_entry(&p); 21418c2ecf20Sopenharmony_ci 21428c2ecf20Sopenharmony_ci if (cpu_has_rixi && !cpu_has_rixiex) { 21438c2ecf20Sopenharmony_ci /* 21448c2ecf20Sopenharmony_ci * If the page is not _PAGE_VALID, RI or XI could not 21458c2ecf20Sopenharmony_ci * have triggered it. Skip the expensive test.. 21468c2ecf20Sopenharmony_ci */ 21478c2ecf20Sopenharmony_ci if (use_bbit_insns()) { 21488c2ecf20Sopenharmony_ci uasm_il_bbit0(&p, &r, wr.r1, ilog2(_PAGE_VALID), 21498c2ecf20Sopenharmony_ci label_tlbl_goaround1); 21508c2ecf20Sopenharmony_ci } else { 21518c2ecf20Sopenharmony_ci uasm_i_andi(&p, wr.r3, wr.r1, _PAGE_VALID); 21528c2ecf20Sopenharmony_ci uasm_il_beqz(&p, &r, wr.r3, label_tlbl_goaround1); 21538c2ecf20Sopenharmony_ci } 21548c2ecf20Sopenharmony_ci uasm_i_nop(&p); 21558c2ecf20Sopenharmony_ci 21568c2ecf20Sopenharmony_ci /* 21578c2ecf20Sopenharmony_ci * Warn if something may race with us & replace the TLB entry 21588c2ecf20Sopenharmony_ci * before we read it here. Everything with such races should 21598c2ecf20Sopenharmony_ci * also have dedicated RiXi exception handlers, so this 21608c2ecf20Sopenharmony_ci * shouldn't be hit. 21618c2ecf20Sopenharmony_ci */ 21628c2ecf20Sopenharmony_ci WARN(cpu_has_tlbex_tlbp_race(), "Unhandled race in RiXi path"); 21638c2ecf20Sopenharmony_ci 21648c2ecf20Sopenharmony_ci uasm_i_tlbr(&p); 21658c2ecf20Sopenharmony_ci 21668c2ecf20Sopenharmony_ci switch (current_cpu_type()) { 21678c2ecf20Sopenharmony_ci default: 21688c2ecf20Sopenharmony_ci if (cpu_has_mips_r2_exec_hazard) { 21698c2ecf20Sopenharmony_ci uasm_i_ehb(&p); 21708c2ecf20Sopenharmony_ci 21718c2ecf20Sopenharmony_ci case CPU_CAVIUM_OCTEON: 21728c2ecf20Sopenharmony_ci case CPU_CAVIUM_OCTEON_PLUS: 21738c2ecf20Sopenharmony_ci case CPU_CAVIUM_OCTEON2: 21748c2ecf20Sopenharmony_ci break; 21758c2ecf20Sopenharmony_ci } 21768c2ecf20Sopenharmony_ci } 21778c2ecf20Sopenharmony_ci 21788c2ecf20Sopenharmony_ci /* Examine entrylo 0 or 1 based on ptr. */ 21798c2ecf20Sopenharmony_ci if (use_bbit_insns()) { 21808c2ecf20Sopenharmony_ci uasm_i_bbit0(&p, wr.r2, ilog2(sizeof(pte_t)), 8); 21818c2ecf20Sopenharmony_ci } else { 21828c2ecf20Sopenharmony_ci uasm_i_andi(&p, wr.r3, wr.r2, sizeof(pte_t)); 21838c2ecf20Sopenharmony_ci uasm_i_beqz(&p, wr.r3, 8); 21848c2ecf20Sopenharmony_ci } 21858c2ecf20Sopenharmony_ci /* load it in the delay slot*/ 21868c2ecf20Sopenharmony_ci UASM_i_MFC0(&p, wr.r3, C0_ENTRYLO0); 21878c2ecf20Sopenharmony_ci /* load it if ptr is odd */ 21888c2ecf20Sopenharmony_ci UASM_i_MFC0(&p, wr.r3, C0_ENTRYLO1); 21898c2ecf20Sopenharmony_ci /* 21908c2ecf20Sopenharmony_ci * If the entryLo (now in wr.r3) is valid (bit 1), RI or 21918c2ecf20Sopenharmony_ci * XI must have triggered it. 21928c2ecf20Sopenharmony_ci */ 21938c2ecf20Sopenharmony_ci if (use_bbit_insns()) { 21948c2ecf20Sopenharmony_ci uasm_il_bbit1(&p, &r, wr.r3, 1, label_nopage_tlbl); 21958c2ecf20Sopenharmony_ci uasm_i_nop(&p); 21968c2ecf20Sopenharmony_ci uasm_l_tlbl_goaround1(&l, p); 21978c2ecf20Sopenharmony_ci } else { 21988c2ecf20Sopenharmony_ci uasm_i_andi(&p, wr.r3, wr.r3, 2); 21998c2ecf20Sopenharmony_ci uasm_il_bnez(&p, &r, wr.r3, label_nopage_tlbl); 22008c2ecf20Sopenharmony_ci uasm_i_nop(&p); 22018c2ecf20Sopenharmony_ci } 22028c2ecf20Sopenharmony_ci uasm_l_tlbl_goaround1(&l, p); 22038c2ecf20Sopenharmony_ci } 22048c2ecf20Sopenharmony_ci build_make_valid(&p, &r, wr.r1, wr.r2, wr.r3); 22058c2ecf20Sopenharmony_ci build_r4000_tlbchange_handler_tail(&p, &l, &r, wr.r1, wr.r2); 22068c2ecf20Sopenharmony_ci 22078c2ecf20Sopenharmony_ci#ifdef CONFIG_MIPS_HUGE_TLB_SUPPORT 22088c2ecf20Sopenharmony_ci /* 22098c2ecf20Sopenharmony_ci * This is the entry point when build_r4000_tlbchange_handler_head 22108c2ecf20Sopenharmony_ci * spots a huge page. 22118c2ecf20Sopenharmony_ci */ 22128c2ecf20Sopenharmony_ci uasm_l_tlb_huge_update(&l, p); 22138c2ecf20Sopenharmony_ci iPTE_LW(&p, wr.r1, wr.r2); 22148c2ecf20Sopenharmony_ci build_pte_present(&p, &r, wr.r1, wr.r2, wr.r3, label_nopage_tlbl); 22158c2ecf20Sopenharmony_ci build_tlb_probe_entry(&p); 22168c2ecf20Sopenharmony_ci 22178c2ecf20Sopenharmony_ci if (cpu_has_rixi && !cpu_has_rixiex) { 22188c2ecf20Sopenharmony_ci /* 22198c2ecf20Sopenharmony_ci * If the page is not _PAGE_VALID, RI or XI could not 22208c2ecf20Sopenharmony_ci * have triggered it. Skip the expensive test.. 22218c2ecf20Sopenharmony_ci */ 22228c2ecf20Sopenharmony_ci if (use_bbit_insns()) { 22238c2ecf20Sopenharmony_ci uasm_il_bbit0(&p, &r, wr.r1, ilog2(_PAGE_VALID), 22248c2ecf20Sopenharmony_ci label_tlbl_goaround2); 22258c2ecf20Sopenharmony_ci } else { 22268c2ecf20Sopenharmony_ci uasm_i_andi(&p, wr.r3, wr.r1, _PAGE_VALID); 22278c2ecf20Sopenharmony_ci uasm_il_beqz(&p, &r, wr.r3, label_tlbl_goaround2); 22288c2ecf20Sopenharmony_ci } 22298c2ecf20Sopenharmony_ci uasm_i_nop(&p); 22308c2ecf20Sopenharmony_ci 22318c2ecf20Sopenharmony_ci /* 22328c2ecf20Sopenharmony_ci * Warn if something may race with us & replace the TLB entry 22338c2ecf20Sopenharmony_ci * before we read it here. Everything with such races should 22348c2ecf20Sopenharmony_ci * also have dedicated RiXi exception handlers, so this 22358c2ecf20Sopenharmony_ci * shouldn't be hit. 22368c2ecf20Sopenharmony_ci */ 22378c2ecf20Sopenharmony_ci WARN(cpu_has_tlbex_tlbp_race(), "Unhandled race in RiXi path"); 22388c2ecf20Sopenharmony_ci 22398c2ecf20Sopenharmony_ci uasm_i_tlbr(&p); 22408c2ecf20Sopenharmony_ci 22418c2ecf20Sopenharmony_ci switch (current_cpu_type()) { 22428c2ecf20Sopenharmony_ci default: 22438c2ecf20Sopenharmony_ci if (cpu_has_mips_r2_exec_hazard) { 22448c2ecf20Sopenharmony_ci uasm_i_ehb(&p); 22458c2ecf20Sopenharmony_ci 22468c2ecf20Sopenharmony_ci case CPU_CAVIUM_OCTEON: 22478c2ecf20Sopenharmony_ci case CPU_CAVIUM_OCTEON_PLUS: 22488c2ecf20Sopenharmony_ci case CPU_CAVIUM_OCTEON2: 22498c2ecf20Sopenharmony_ci break; 22508c2ecf20Sopenharmony_ci } 22518c2ecf20Sopenharmony_ci } 22528c2ecf20Sopenharmony_ci 22538c2ecf20Sopenharmony_ci /* Examine entrylo 0 or 1 based on ptr. */ 22548c2ecf20Sopenharmony_ci if (use_bbit_insns()) { 22558c2ecf20Sopenharmony_ci uasm_i_bbit0(&p, wr.r2, ilog2(sizeof(pte_t)), 8); 22568c2ecf20Sopenharmony_ci } else { 22578c2ecf20Sopenharmony_ci uasm_i_andi(&p, wr.r3, wr.r2, sizeof(pte_t)); 22588c2ecf20Sopenharmony_ci uasm_i_beqz(&p, wr.r3, 8); 22598c2ecf20Sopenharmony_ci } 22608c2ecf20Sopenharmony_ci /* load it in the delay slot*/ 22618c2ecf20Sopenharmony_ci UASM_i_MFC0(&p, wr.r3, C0_ENTRYLO0); 22628c2ecf20Sopenharmony_ci /* load it if ptr is odd */ 22638c2ecf20Sopenharmony_ci UASM_i_MFC0(&p, wr.r3, C0_ENTRYLO1); 22648c2ecf20Sopenharmony_ci /* 22658c2ecf20Sopenharmony_ci * If the entryLo (now in wr.r3) is valid (bit 1), RI or 22668c2ecf20Sopenharmony_ci * XI must have triggered it. 22678c2ecf20Sopenharmony_ci */ 22688c2ecf20Sopenharmony_ci if (use_bbit_insns()) { 22698c2ecf20Sopenharmony_ci uasm_il_bbit0(&p, &r, wr.r3, 1, label_tlbl_goaround2); 22708c2ecf20Sopenharmony_ci } else { 22718c2ecf20Sopenharmony_ci uasm_i_andi(&p, wr.r3, wr.r3, 2); 22728c2ecf20Sopenharmony_ci uasm_il_beqz(&p, &r, wr.r3, label_tlbl_goaround2); 22738c2ecf20Sopenharmony_ci } 22748c2ecf20Sopenharmony_ci if (PM_DEFAULT_MASK == 0) 22758c2ecf20Sopenharmony_ci uasm_i_nop(&p); 22768c2ecf20Sopenharmony_ci /* 22778c2ecf20Sopenharmony_ci * We clobbered C0_PAGEMASK, restore it. On the other branch 22788c2ecf20Sopenharmony_ci * it is restored in build_huge_tlb_write_entry. 22798c2ecf20Sopenharmony_ci */ 22808c2ecf20Sopenharmony_ci build_restore_pagemask(&p, &r, wr.r3, label_nopage_tlbl, 0); 22818c2ecf20Sopenharmony_ci 22828c2ecf20Sopenharmony_ci uasm_l_tlbl_goaround2(&l, p); 22838c2ecf20Sopenharmony_ci } 22848c2ecf20Sopenharmony_ci uasm_i_ori(&p, wr.r1, wr.r1, (_PAGE_ACCESSED | _PAGE_VALID)); 22858c2ecf20Sopenharmony_ci build_huge_handler_tail(&p, &r, &l, wr.r1, wr.r2, 1); 22868c2ecf20Sopenharmony_ci#endif 22878c2ecf20Sopenharmony_ci 22888c2ecf20Sopenharmony_ci uasm_l_nopage_tlbl(&l, p); 22898c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_CPU_LOONGSON3_WORKAROUNDS)) 22908c2ecf20Sopenharmony_ci uasm_i_sync(&p, 0); 22918c2ecf20Sopenharmony_ci build_restore_work_registers(&p); 22928c2ecf20Sopenharmony_ci#ifdef CONFIG_CPU_MICROMIPS 22938c2ecf20Sopenharmony_ci if ((unsigned long)tlb_do_page_fault_0 & 1) { 22948c2ecf20Sopenharmony_ci uasm_i_lui(&p, K0, uasm_rel_hi((long)tlb_do_page_fault_0)); 22958c2ecf20Sopenharmony_ci uasm_i_addiu(&p, K0, K0, uasm_rel_lo((long)tlb_do_page_fault_0)); 22968c2ecf20Sopenharmony_ci uasm_i_jr(&p, K0); 22978c2ecf20Sopenharmony_ci } else 22988c2ecf20Sopenharmony_ci#endif 22998c2ecf20Sopenharmony_ci uasm_i_j(&p, (unsigned long)tlb_do_page_fault_0 & 0x0fffffff); 23008c2ecf20Sopenharmony_ci uasm_i_nop(&p); 23018c2ecf20Sopenharmony_ci 23028c2ecf20Sopenharmony_ci if (p >= (u32 *)handle_tlbl_end) 23038c2ecf20Sopenharmony_ci panic("TLB load handler fastpath space exceeded"); 23048c2ecf20Sopenharmony_ci 23058c2ecf20Sopenharmony_ci uasm_resolve_relocs(relocs, labels); 23068c2ecf20Sopenharmony_ci pr_debug("Wrote TLB load handler fastpath (%u instructions).\n", 23078c2ecf20Sopenharmony_ci (unsigned int)(p - (u32 *)handle_tlbl)); 23088c2ecf20Sopenharmony_ci 23098c2ecf20Sopenharmony_ci dump_handler("r4000_tlb_load", handle_tlbl, handle_tlbl_end); 23108c2ecf20Sopenharmony_ci} 23118c2ecf20Sopenharmony_ci 23128c2ecf20Sopenharmony_cistatic void build_r4000_tlb_store_handler(void) 23138c2ecf20Sopenharmony_ci{ 23148c2ecf20Sopenharmony_ci u32 *p = (u32 *)msk_isa16_mode((ulong)handle_tlbs); 23158c2ecf20Sopenharmony_ci struct uasm_label *l = labels; 23168c2ecf20Sopenharmony_ci struct uasm_reloc *r = relocs; 23178c2ecf20Sopenharmony_ci struct work_registers wr; 23188c2ecf20Sopenharmony_ci 23198c2ecf20Sopenharmony_ci memset(p, 0, handle_tlbs_end - (char *)p); 23208c2ecf20Sopenharmony_ci memset(labels, 0, sizeof(labels)); 23218c2ecf20Sopenharmony_ci memset(relocs, 0, sizeof(relocs)); 23228c2ecf20Sopenharmony_ci 23238c2ecf20Sopenharmony_ci wr = build_r4000_tlbchange_handler_head(&p, &l, &r); 23248c2ecf20Sopenharmony_ci build_pte_writable(&p, &r, wr.r1, wr.r2, wr.r3, label_nopage_tlbs); 23258c2ecf20Sopenharmony_ci if (m4kc_tlbp_war()) 23268c2ecf20Sopenharmony_ci build_tlb_probe_entry(&p); 23278c2ecf20Sopenharmony_ci build_make_write(&p, &r, wr.r1, wr.r2, wr.r3); 23288c2ecf20Sopenharmony_ci build_r4000_tlbchange_handler_tail(&p, &l, &r, wr.r1, wr.r2); 23298c2ecf20Sopenharmony_ci 23308c2ecf20Sopenharmony_ci#ifdef CONFIG_MIPS_HUGE_TLB_SUPPORT 23318c2ecf20Sopenharmony_ci /* 23328c2ecf20Sopenharmony_ci * This is the entry point when 23338c2ecf20Sopenharmony_ci * build_r4000_tlbchange_handler_head spots a huge page. 23348c2ecf20Sopenharmony_ci */ 23358c2ecf20Sopenharmony_ci uasm_l_tlb_huge_update(&l, p); 23368c2ecf20Sopenharmony_ci iPTE_LW(&p, wr.r1, wr.r2); 23378c2ecf20Sopenharmony_ci build_pte_writable(&p, &r, wr.r1, wr.r2, wr.r3, label_nopage_tlbs); 23388c2ecf20Sopenharmony_ci build_tlb_probe_entry(&p); 23398c2ecf20Sopenharmony_ci uasm_i_ori(&p, wr.r1, wr.r1, 23408c2ecf20Sopenharmony_ci _PAGE_ACCESSED | _PAGE_MODIFIED | _PAGE_VALID | _PAGE_DIRTY); 23418c2ecf20Sopenharmony_ci build_huge_handler_tail(&p, &r, &l, wr.r1, wr.r2, 1); 23428c2ecf20Sopenharmony_ci#endif 23438c2ecf20Sopenharmony_ci 23448c2ecf20Sopenharmony_ci uasm_l_nopage_tlbs(&l, p); 23458c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_CPU_LOONGSON3_WORKAROUNDS)) 23468c2ecf20Sopenharmony_ci uasm_i_sync(&p, 0); 23478c2ecf20Sopenharmony_ci build_restore_work_registers(&p); 23488c2ecf20Sopenharmony_ci#ifdef CONFIG_CPU_MICROMIPS 23498c2ecf20Sopenharmony_ci if ((unsigned long)tlb_do_page_fault_1 & 1) { 23508c2ecf20Sopenharmony_ci uasm_i_lui(&p, K0, uasm_rel_hi((long)tlb_do_page_fault_1)); 23518c2ecf20Sopenharmony_ci uasm_i_addiu(&p, K0, K0, uasm_rel_lo((long)tlb_do_page_fault_1)); 23528c2ecf20Sopenharmony_ci uasm_i_jr(&p, K0); 23538c2ecf20Sopenharmony_ci } else 23548c2ecf20Sopenharmony_ci#endif 23558c2ecf20Sopenharmony_ci uasm_i_j(&p, (unsigned long)tlb_do_page_fault_1 & 0x0fffffff); 23568c2ecf20Sopenharmony_ci uasm_i_nop(&p); 23578c2ecf20Sopenharmony_ci 23588c2ecf20Sopenharmony_ci if (p >= (u32 *)handle_tlbs_end) 23598c2ecf20Sopenharmony_ci panic("TLB store handler fastpath space exceeded"); 23608c2ecf20Sopenharmony_ci 23618c2ecf20Sopenharmony_ci uasm_resolve_relocs(relocs, labels); 23628c2ecf20Sopenharmony_ci pr_debug("Wrote TLB store handler fastpath (%u instructions).\n", 23638c2ecf20Sopenharmony_ci (unsigned int)(p - (u32 *)handle_tlbs)); 23648c2ecf20Sopenharmony_ci 23658c2ecf20Sopenharmony_ci dump_handler("r4000_tlb_store", handle_tlbs, handle_tlbs_end); 23668c2ecf20Sopenharmony_ci} 23678c2ecf20Sopenharmony_ci 23688c2ecf20Sopenharmony_cistatic void build_r4000_tlb_modify_handler(void) 23698c2ecf20Sopenharmony_ci{ 23708c2ecf20Sopenharmony_ci u32 *p = (u32 *)msk_isa16_mode((ulong)handle_tlbm); 23718c2ecf20Sopenharmony_ci struct uasm_label *l = labels; 23728c2ecf20Sopenharmony_ci struct uasm_reloc *r = relocs; 23738c2ecf20Sopenharmony_ci struct work_registers wr; 23748c2ecf20Sopenharmony_ci 23758c2ecf20Sopenharmony_ci memset(p, 0, handle_tlbm_end - (char *)p); 23768c2ecf20Sopenharmony_ci memset(labels, 0, sizeof(labels)); 23778c2ecf20Sopenharmony_ci memset(relocs, 0, sizeof(relocs)); 23788c2ecf20Sopenharmony_ci 23798c2ecf20Sopenharmony_ci wr = build_r4000_tlbchange_handler_head(&p, &l, &r); 23808c2ecf20Sopenharmony_ci build_pte_modifiable(&p, &r, wr.r1, wr.r2, wr.r3, label_nopage_tlbm); 23818c2ecf20Sopenharmony_ci if (m4kc_tlbp_war()) 23828c2ecf20Sopenharmony_ci build_tlb_probe_entry(&p); 23838c2ecf20Sopenharmony_ci /* Present and writable bits set, set accessed and dirty bits. */ 23848c2ecf20Sopenharmony_ci build_make_write(&p, &r, wr.r1, wr.r2, wr.r3); 23858c2ecf20Sopenharmony_ci build_r4000_tlbchange_handler_tail(&p, &l, &r, wr.r1, wr.r2); 23868c2ecf20Sopenharmony_ci 23878c2ecf20Sopenharmony_ci#ifdef CONFIG_MIPS_HUGE_TLB_SUPPORT 23888c2ecf20Sopenharmony_ci /* 23898c2ecf20Sopenharmony_ci * This is the entry point when 23908c2ecf20Sopenharmony_ci * build_r4000_tlbchange_handler_head spots a huge page. 23918c2ecf20Sopenharmony_ci */ 23928c2ecf20Sopenharmony_ci uasm_l_tlb_huge_update(&l, p); 23938c2ecf20Sopenharmony_ci iPTE_LW(&p, wr.r1, wr.r2); 23948c2ecf20Sopenharmony_ci build_pte_modifiable(&p, &r, wr.r1, wr.r2, wr.r3, label_nopage_tlbm); 23958c2ecf20Sopenharmony_ci build_tlb_probe_entry(&p); 23968c2ecf20Sopenharmony_ci uasm_i_ori(&p, wr.r1, wr.r1, 23978c2ecf20Sopenharmony_ci _PAGE_ACCESSED | _PAGE_MODIFIED | _PAGE_VALID | _PAGE_DIRTY); 23988c2ecf20Sopenharmony_ci build_huge_handler_tail(&p, &r, &l, wr.r1, wr.r2, 0); 23998c2ecf20Sopenharmony_ci#endif 24008c2ecf20Sopenharmony_ci 24018c2ecf20Sopenharmony_ci uasm_l_nopage_tlbm(&l, p); 24028c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_CPU_LOONGSON3_WORKAROUNDS)) 24038c2ecf20Sopenharmony_ci uasm_i_sync(&p, 0); 24048c2ecf20Sopenharmony_ci build_restore_work_registers(&p); 24058c2ecf20Sopenharmony_ci#ifdef CONFIG_CPU_MICROMIPS 24068c2ecf20Sopenharmony_ci if ((unsigned long)tlb_do_page_fault_1 & 1) { 24078c2ecf20Sopenharmony_ci uasm_i_lui(&p, K0, uasm_rel_hi((long)tlb_do_page_fault_1)); 24088c2ecf20Sopenharmony_ci uasm_i_addiu(&p, K0, K0, uasm_rel_lo((long)tlb_do_page_fault_1)); 24098c2ecf20Sopenharmony_ci uasm_i_jr(&p, K0); 24108c2ecf20Sopenharmony_ci } else 24118c2ecf20Sopenharmony_ci#endif 24128c2ecf20Sopenharmony_ci uasm_i_j(&p, (unsigned long)tlb_do_page_fault_1 & 0x0fffffff); 24138c2ecf20Sopenharmony_ci uasm_i_nop(&p); 24148c2ecf20Sopenharmony_ci 24158c2ecf20Sopenharmony_ci if (p >= (u32 *)handle_tlbm_end) 24168c2ecf20Sopenharmony_ci panic("TLB modify handler fastpath space exceeded"); 24178c2ecf20Sopenharmony_ci 24188c2ecf20Sopenharmony_ci uasm_resolve_relocs(relocs, labels); 24198c2ecf20Sopenharmony_ci pr_debug("Wrote TLB modify handler fastpath (%u instructions).\n", 24208c2ecf20Sopenharmony_ci (unsigned int)(p - (u32 *)handle_tlbm)); 24218c2ecf20Sopenharmony_ci 24228c2ecf20Sopenharmony_ci dump_handler("r4000_tlb_modify", handle_tlbm, handle_tlbm_end); 24238c2ecf20Sopenharmony_ci} 24248c2ecf20Sopenharmony_ci 24258c2ecf20Sopenharmony_cistatic void flush_tlb_handlers(void) 24268c2ecf20Sopenharmony_ci{ 24278c2ecf20Sopenharmony_ci local_flush_icache_range((unsigned long)handle_tlbl, 24288c2ecf20Sopenharmony_ci (unsigned long)handle_tlbl_end); 24298c2ecf20Sopenharmony_ci local_flush_icache_range((unsigned long)handle_tlbs, 24308c2ecf20Sopenharmony_ci (unsigned long)handle_tlbs_end); 24318c2ecf20Sopenharmony_ci local_flush_icache_range((unsigned long)handle_tlbm, 24328c2ecf20Sopenharmony_ci (unsigned long)handle_tlbm_end); 24338c2ecf20Sopenharmony_ci local_flush_icache_range((unsigned long)tlbmiss_handler_setup_pgd, 24348c2ecf20Sopenharmony_ci (unsigned long)tlbmiss_handler_setup_pgd_end); 24358c2ecf20Sopenharmony_ci} 24368c2ecf20Sopenharmony_ci 24378c2ecf20Sopenharmony_cistatic void print_htw_config(void) 24388c2ecf20Sopenharmony_ci{ 24398c2ecf20Sopenharmony_ci unsigned long config; 24408c2ecf20Sopenharmony_ci unsigned int pwctl; 24418c2ecf20Sopenharmony_ci const int field = 2 * sizeof(unsigned long); 24428c2ecf20Sopenharmony_ci 24438c2ecf20Sopenharmony_ci config = read_c0_pwfield(); 24448c2ecf20Sopenharmony_ci pr_debug("PWField (0x%0*lx): GDI: 0x%02lx UDI: 0x%02lx MDI: 0x%02lx PTI: 0x%02lx PTEI: 0x%02lx\n", 24458c2ecf20Sopenharmony_ci field, config, 24468c2ecf20Sopenharmony_ci (config & MIPS_PWFIELD_GDI_MASK) >> MIPS_PWFIELD_GDI_SHIFT, 24478c2ecf20Sopenharmony_ci (config & MIPS_PWFIELD_UDI_MASK) >> MIPS_PWFIELD_UDI_SHIFT, 24488c2ecf20Sopenharmony_ci (config & MIPS_PWFIELD_MDI_MASK) >> MIPS_PWFIELD_MDI_SHIFT, 24498c2ecf20Sopenharmony_ci (config & MIPS_PWFIELD_PTI_MASK) >> MIPS_PWFIELD_PTI_SHIFT, 24508c2ecf20Sopenharmony_ci (config & MIPS_PWFIELD_PTEI_MASK) >> MIPS_PWFIELD_PTEI_SHIFT); 24518c2ecf20Sopenharmony_ci 24528c2ecf20Sopenharmony_ci config = read_c0_pwsize(); 24538c2ecf20Sopenharmony_ci pr_debug("PWSize (0x%0*lx): PS: 0x%lx GDW: 0x%02lx UDW: 0x%02lx MDW: 0x%02lx PTW: 0x%02lx PTEW: 0x%02lx\n", 24548c2ecf20Sopenharmony_ci field, config, 24558c2ecf20Sopenharmony_ci (config & MIPS_PWSIZE_PS_MASK) >> MIPS_PWSIZE_PS_SHIFT, 24568c2ecf20Sopenharmony_ci (config & MIPS_PWSIZE_GDW_MASK) >> MIPS_PWSIZE_GDW_SHIFT, 24578c2ecf20Sopenharmony_ci (config & MIPS_PWSIZE_UDW_MASK) >> MIPS_PWSIZE_UDW_SHIFT, 24588c2ecf20Sopenharmony_ci (config & MIPS_PWSIZE_MDW_MASK) >> MIPS_PWSIZE_MDW_SHIFT, 24598c2ecf20Sopenharmony_ci (config & MIPS_PWSIZE_PTW_MASK) >> MIPS_PWSIZE_PTW_SHIFT, 24608c2ecf20Sopenharmony_ci (config & MIPS_PWSIZE_PTEW_MASK) >> MIPS_PWSIZE_PTEW_SHIFT); 24618c2ecf20Sopenharmony_ci 24628c2ecf20Sopenharmony_ci pwctl = read_c0_pwctl(); 24638c2ecf20Sopenharmony_ci pr_debug("PWCtl (0x%x): PWEn: 0x%x XK: 0x%x XS: 0x%x XU: 0x%x DPH: 0x%x HugePg: 0x%x Psn: 0x%x\n", 24648c2ecf20Sopenharmony_ci pwctl, 24658c2ecf20Sopenharmony_ci (pwctl & MIPS_PWCTL_PWEN_MASK) >> MIPS_PWCTL_PWEN_SHIFT, 24668c2ecf20Sopenharmony_ci (pwctl & MIPS_PWCTL_XK_MASK) >> MIPS_PWCTL_XK_SHIFT, 24678c2ecf20Sopenharmony_ci (pwctl & MIPS_PWCTL_XS_MASK) >> MIPS_PWCTL_XS_SHIFT, 24688c2ecf20Sopenharmony_ci (pwctl & MIPS_PWCTL_XU_MASK) >> MIPS_PWCTL_XU_SHIFT, 24698c2ecf20Sopenharmony_ci (pwctl & MIPS_PWCTL_DPH_MASK) >> MIPS_PWCTL_DPH_SHIFT, 24708c2ecf20Sopenharmony_ci (pwctl & MIPS_PWCTL_HUGEPG_MASK) >> MIPS_PWCTL_HUGEPG_SHIFT, 24718c2ecf20Sopenharmony_ci (pwctl & MIPS_PWCTL_PSN_MASK) >> MIPS_PWCTL_PSN_SHIFT); 24728c2ecf20Sopenharmony_ci} 24738c2ecf20Sopenharmony_ci 24748c2ecf20Sopenharmony_cistatic void config_htw_params(void) 24758c2ecf20Sopenharmony_ci{ 24768c2ecf20Sopenharmony_ci unsigned long pwfield, pwsize, ptei; 24778c2ecf20Sopenharmony_ci unsigned int config; 24788c2ecf20Sopenharmony_ci 24798c2ecf20Sopenharmony_ci /* 24808c2ecf20Sopenharmony_ci * We are using 2-level page tables, so we only need to 24818c2ecf20Sopenharmony_ci * setup GDW and PTW appropriately. UDW and MDW will remain 0. 24828c2ecf20Sopenharmony_ci * The default value of GDI/UDI/MDI/PTI is 0xc. It is illegal to 24838c2ecf20Sopenharmony_ci * write values less than 0xc in these fields because the entire 24848c2ecf20Sopenharmony_ci * write will be dropped. As a result of which, we must preserve 24858c2ecf20Sopenharmony_ci * the original reset values and overwrite only what we really want. 24868c2ecf20Sopenharmony_ci */ 24878c2ecf20Sopenharmony_ci 24888c2ecf20Sopenharmony_ci pwfield = read_c0_pwfield(); 24898c2ecf20Sopenharmony_ci /* re-initialize the GDI field */ 24908c2ecf20Sopenharmony_ci pwfield &= ~MIPS_PWFIELD_GDI_MASK; 24918c2ecf20Sopenharmony_ci pwfield |= PGDIR_SHIFT << MIPS_PWFIELD_GDI_SHIFT; 24928c2ecf20Sopenharmony_ci /* re-initialize the PTI field including the even/odd bit */ 24938c2ecf20Sopenharmony_ci pwfield &= ~MIPS_PWFIELD_PTI_MASK; 24948c2ecf20Sopenharmony_ci pwfield |= PAGE_SHIFT << MIPS_PWFIELD_PTI_SHIFT; 24958c2ecf20Sopenharmony_ci if (CONFIG_PGTABLE_LEVELS >= 3) { 24968c2ecf20Sopenharmony_ci pwfield &= ~MIPS_PWFIELD_MDI_MASK; 24978c2ecf20Sopenharmony_ci pwfield |= PMD_SHIFT << MIPS_PWFIELD_MDI_SHIFT; 24988c2ecf20Sopenharmony_ci } 24998c2ecf20Sopenharmony_ci /* Set the PTEI right shift */ 25008c2ecf20Sopenharmony_ci ptei = _PAGE_GLOBAL_SHIFT << MIPS_PWFIELD_PTEI_SHIFT; 25018c2ecf20Sopenharmony_ci pwfield |= ptei; 25028c2ecf20Sopenharmony_ci write_c0_pwfield(pwfield); 25038c2ecf20Sopenharmony_ci /* Check whether the PTEI value is supported */ 25048c2ecf20Sopenharmony_ci back_to_back_c0_hazard(); 25058c2ecf20Sopenharmony_ci pwfield = read_c0_pwfield(); 25068c2ecf20Sopenharmony_ci if (((pwfield & MIPS_PWFIELD_PTEI_MASK) << MIPS_PWFIELD_PTEI_SHIFT) 25078c2ecf20Sopenharmony_ci != ptei) { 25088c2ecf20Sopenharmony_ci pr_warn("Unsupported PTEI field value: 0x%lx. HTW will not be enabled", 25098c2ecf20Sopenharmony_ci ptei); 25108c2ecf20Sopenharmony_ci /* 25118c2ecf20Sopenharmony_ci * Drop option to avoid HTW being enabled via another path 25128c2ecf20Sopenharmony_ci * (eg htw_reset()) 25138c2ecf20Sopenharmony_ci */ 25148c2ecf20Sopenharmony_ci current_cpu_data.options &= ~MIPS_CPU_HTW; 25158c2ecf20Sopenharmony_ci return; 25168c2ecf20Sopenharmony_ci } 25178c2ecf20Sopenharmony_ci 25188c2ecf20Sopenharmony_ci pwsize = ilog2(PTRS_PER_PGD) << MIPS_PWSIZE_GDW_SHIFT; 25198c2ecf20Sopenharmony_ci pwsize |= ilog2(PTRS_PER_PTE) << MIPS_PWSIZE_PTW_SHIFT; 25208c2ecf20Sopenharmony_ci if (CONFIG_PGTABLE_LEVELS >= 3) 25218c2ecf20Sopenharmony_ci pwsize |= ilog2(PTRS_PER_PMD) << MIPS_PWSIZE_MDW_SHIFT; 25228c2ecf20Sopenharmony_ci 25238c2ecf20Sopenharmony_ci /* Set pointer size to size of directory pointers */ 25248c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_64BIT)) 25258c2ecf20Sopenharmony_ci pwsize |= MIPS_PWSIZE_PS_MASK; 25268c2ecf20Sopenharmony_ci /* PTEs may be multiple pointers long (e.g. with XPA) */ 25278c2ecf20Sopenharmony_ci pwsize |= ((PTE_T_LOG2 - PGD_T_LOG2) << MIPS_PWSIZE_PTEW_SHIFT) 25288c2ecf20Sopenharmony_ci & MIPS_PWSIZE_PTEW_MASK; 25298c2ecf20Sopenharmony_ci 25308c2ecf20Sopenharmony_ci write_c0_pwsize(pwsize); 25318c2ecf20Sopenharmony_ci 25328c2ecf20Sopenharmony_ci /* Make sure everything is set before we enable the HTW */ 25338c2ecf20Sopenharmony_ci back_to_back_c0_hazard(); 25348c2ecf20Sopenharmony_ci 25358c2ecf20Sopenharmony_ci /* 25368c2ecf20Sopenharmony_ci * Enable HTW (and only for XUSeg on 64-bit), and disable the rest of 25378c2ecf20Sopenharmony_ci * the pwctl fields. 25388c2ecf20Sopenharmony_ci */ 25398c2ecf20Sopenharmony_ci config = 1 << MIPS_PWCTL_PWEN_SHIFT; 25408c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_64BIT)) 25418c2ecf20Sopenharmony_ci config |= MIPS_PWCTL_XU_MASK; 25428c2ecf20Sopenharmony_ci write_c0_pwctl(config); 25438c2ecf20Sopenharmony_ci pr_info("Hardware Page Table Walker enabled\n"); 25448c2ecf20Sopenharmony_ci 25458c2ecf20Sopenharmony_ci print_htw_config(); 25468c2ecf20Sopenharmony_ci} 25478c2ecf20Sopenharmony_ci 25488c2ecf20Sopenharmony_cistatic void config_xpa_params(void) 25498c2ecf20Sopenharmony_ci{ 25508c2ecf20Sopenharmony_ci#ifdef CONFIG_XPA 25518c2ecf20Sopenharmony_ci unsigned int pagegrain; 25528c2ecf20Sopenharmony_ci 25538c2ecf20Sopenharmony_ci if (mips_xpa_disabled) { 25548c2ecf20Sopenharmony_ci pr_info("Extended Physical Addressing (XPA) disabled\n"); 25558c2ecf20Sopenharmony_ci return; 25568c2ecf20Sopenharmony_ci } 25578c2ecf20Sopenharmony_ci 25588c2ecf20Sopenharmony_ci pagegrain = read_c0_pagegrain(); 25598c2ecf20Sopenharmony_ci write_c0_pagegrain(pagegrain | PG_ELPA); 25608c2ecf20Sopenharmony_ci back_to_back_c0_hazard(); 25618c2ecf20Sopenharmony_ci pagegrain = read_c0_pagegrain(); 25628c2ecf20Sopenharmony_ci 25638c2ecf20Sopenharmony_ci if (pagegrain & PG_ELPA) 25648c2ecf20Sopenharmony_ci pr_info("Extended Physical Addressing (XPA) enabled\n"); 25658c2ecf20Sopenharmony_ci else 25668c2ecf20Sopenharmony_ci panic("Extended Physical Addressing (XPA) disabled"); 25678c2ecf20Sopenharmony_ci#endif 25688c2ecf20Sopenharmony_ci} 25698c2ecf20Sopenharmony_ci 25708c2ecf20Sopenharmony_cistatic void check_pabits(void) 25718c2ecf20Sopenharmony_ci{ 25728c2ecf20Sopenharmony_ci unsigned long entry; 25738c2ecf20Sopenharmony_ci unsigned pabits, fillbits; 25748c2ecf20Sopenharmony_ci 25758c2ecf20Sopenharmony_ci if (!cpu_has_rixi || _PAGE_NO_EXEC == 0) { 25768c2ecf20Sopenharmony_ci /* 25778c2ecf20Sopenharmony_ci * We'll only be making use of the fact that we can rotate bits 25788c2ecf20Sopenharmony_ci * into the fill if the CPU supports RIXI, so don't bother 25798c2ecf20Sopenharmony_ci * probing this for CPUs which don't. 25808c2ecf20Sopenharmony_ci */ 25818c2ecf20Sopenharmony_ci return; 25828c2ecf20Sopenharmony_ci } 25838c2ecf20Sopenharmony_ci 25848c2ecf20Sopenharmony_ci write_c0_entrylo0(~0ul); 25858c2ecf20Sopenharmony_ci back_to_back_c0_hazard(); 25868c2ecf20Sopenharmony_ci entry = read_c0_entrylo0(); 25878c2ecf20Sopenharmony_ci 25888c2ecf20Sopenharmony_ci /* clear all non-PFN bits */ 25898c2ecf20Sopenharmony_ci entry &= ~((1 << MIPS_ENTRYLO_PFN_SHIFT) - 1); 25908c2ecf20Sopenharmony_ci entry &= ~(MIPS_ENTRYLO_RI | MIPS_ENTRYLO_XI); 25918c2ecf20Sopenharmony_ci 25928c2ecf20Sopenharmony_ci /* find a lower bound on PABITS, and upper bound on fill bits */ 25938c2ecf20Sopenharmony_ci pabits = fls_long(entry) + 6; 25948c2ecf20Sopenharmony_ci fillbits = max_t(int, (int)BITS_PER_LONG - pabits, 0); 25958c2ecf20Sopenharmony_ci 25968c2ecf20Sopenharmony_ci /* minus the RI & XI bits */ 25978c2ecf20Sopenharmony_ci fillbits -= min_t(unsigned, fillbits, 2); 25988c2ecf20Sopenharmony_ci 25998c2ecf20Sopenharmony_ci if (fillbits >= ilog2(_PAGE_NO_EXEC)) 26008c2ecf20Sopenharmony_ci fill_includes_sw_bits = true; 26018c2ecf20Sopenharmony_ci 26028c2ecf20Sopenharmony_ci pr_debug("Entry* registers contain %u fill bits\n", fillbits); 26038c2ecf20Sopenharmony_ci} 26048c2ecf20Sopenharmony_ci 26058c2ecf20Sopenharmony_civoid build_tlb_refill_handler(void) 26068c2ecf20Sopenharmony_ci{ 26078c2ecf20Sopenharmony_ci /* 26088c2ecf20Sopenharmony_ci * The refill handler is generated per-CPU, multi-node systems 26098c2ecf20Sopenharmony_ci * may have local storage for it. The other handlers are only 26108c2ecf20Sopenharmony_ci * needed once. 26118c2ecf20Sopenharmony_ci */ 26128c2ecf20Sopenharmony_ci static int run_once = 0; 26138c2ecf20Sopenharmony_ci 26148c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_XPA) && !cpu_has_rixi) 26158c2ecf20Sopenharmony_ci panic("Kernels supporting XPA currently require CPUs with RIXI"); 26168c2ecf20Sopenharmony_ci 26178c2ecf20Sopenharmony_ci output_pgtable_bits_defines(); 26188c2ecf20Sopenharmony_ci check_pabits(); 26198c2ecf20Sopenharmony_ci 26208c2ecf20Sopenharmony_ci#ifdef CONFIG_64BIT 26218c2ecf20Sopenharmony_ci check_for_high_segbits = current_cpu_data.vmbits > (PGDIR_SHIFT + PGD_ORDER + PAGE_SHIFT - 3); 26228c2ecf20Sopenharmony_ci#endif 26238c2ecf20Sopenharmony_ci 26248c2ecf20Sopenharmony_ci if (cpu_has_3kex) { 26258c2ecf20Sopenharmony_ci#ifndef CONFIG_MIPS_PGD_C0_CONTEXT 26268c2ecf20Sopenharmony_ci if (!run_once) { 26278c2ecf20Sopenharmony_ci build_setup_pgd(); 26288c2ecf20Sopenharmony_ci build_r3000_tlb_refill_handler(); 26298c2ecf20Sopenharmony_ci build_r3000_tlb_load_handler(); 26308c2ecf20Sopenharmony_ci build_r3000_tlb_store_handler(); 26318c2ecf20Sopenharmony_ci build_r3000_tlb_modify_handler(); 26328c2ecf20Sopenharmony_ci flush_tlb_handlers(); 26338c2ecf20Sopenharmony_ci run_once++; 26348c2ecf20Sopenharmony_ci } 26358c2ecf20Sopenharmony_ci#else 26368c2ecf20Sopenharmony_ci panic("No R3000 TLB refill handler"); 26378c2ecf20Sopenharmony_ci#endif 26388c2ecf20Sopenharmony_ci return; 26398c2ecf20Sopenharmony_ci } 26408c2ecf20Sopenharmony_ci 26418c2ecf20Sopenharmony_ci if (cpu_has_ldpte) 26428c2ecf20Sopenharmony_ci setup_pw(); 26438c2ecf20Sopenharmony_ci 26448c2ecf20Sopenharmony_ci if (!run_once) { 26458c2ecf20Sopenharmony_ci scratch_reg = allocate_kscratch(); 26468c2ecf20Sopenharmony_ci build_setup_pgd(); 26478c2ecf20Sopenharmony_ci build_r4000_tlb_load_handler(); 26488c2ecf20Sopenharmony_ci build_r4000_tlb_store_handler(); 26498c2ecf20Sopenharmony_ci build_r4000_tlb_modify_handler(); 26508c2ecf20Sopenharmony_ci if (cpu_has_ldpte) 26518c2ecf20Sopenharmony_ci build_loongson3_tlb_refill_handler(); 26528c2ecf20Sopenharmony_ci else 26538c2ecf20Sopenharmony_ci build_r4000_tlb_refill_handler(); 26548c2ecf20Sopenharmony_ci flush_tlb_handlers(); 26558c2ecf20Sopenharmony_ci run_once++; 26568c2ecf20Sopenharmony_ci } 26578c2ecf20Sopenharmony_ci if (cpu_has_xpa) 26588c2ecf20Sopenharmony_ci config_xpa_params(); 26598c2ecf20Sopenharmony_ci if (cpu_has_htw) 26608c2ecf20Sopenharmony_ci config_htw_params(); 26618c2ecf20Sopenharmony_ci} 2662