162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Dump R4x00 TLB for debugging purposes. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 1994, 1995 by Waldorf Electronics, written by Ralf Baechle. 662306a36Sopenharmony_ci * Copyright (C) 1999 by Silicon Graphics, Inc. 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci#include <linux/kernel.h> 962306a36Sopenharmony_ci#include <linux/mm.h> 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <asm/hazards.h> 1262306a36Sopenharmony_ci#include <asm/mipsregs.h> 1362306a36Sopenharmony_ci#include <asm/mmu_context.h> 1462306a36Sopenharmony_ci#include <asm/page.h> 1562306a36Sopenharmony_ci#include <asm/tlbdebug.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_civoid dump_tlb_regs(void) 1862306a36Sopenharmony_ci{ 1962306a36Sopenharmony_ci const int field = 2 * sizeof(unsigned long); 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci pr_info("Index : %0x\n", read_c0_index()); 2262306a36Sopenharmony_ci pr_info("PageMask : %0x\n", read_c0_pagemask()); 2362306a36Sopenharmony_ci if (cpu_has_guestid) 2462306a36Sopenharmony_ci pr_info("GuestCtl1: %0x\n", read_c0_guestctl1()); 2562306a36Sopenharmony_ci pr_info("EntryHi : %0*lx\n", field, read_c0_entryhi()); 2662306a36Sopenharmony_ci pr_info("EntryLo0 : %0*lx\n", field, read_c0_entrylo0()); 2762306a36Sopenharmony_ci pr_info("EntryLo1 : %0*lx\n", field, read_c0_entrylo1()); 2862306a36Sopenharmony_ci pr_info("Wired : %0x\n", read_c0_wired()); 2962306a36Sopenharmony_ci switch (current_cpu_type()) { 3062306a36Sopenharmony_ci case CPU_R10000: 3162306a36Sopenharmony_ci case CPU_R12000: 3262306a36Sopenharmony_ci case CPU_R14000: 3362306a36Sopenharmony_ci case CPU_R16000: 3462306a36Sopenharmony_ci pr_info("FrameMask: %0x\n", read_c0_framemask()); 3562306a36Sopenharmony_ci break; 3662306a36Sopenharmony_ci } 3762306a36Sopenharmony_ci if (cpu_has_small_pages || cpu_has_rixi || cpu_has_xpa) 3862306a36Sopenharmony_ci pr_info("PageGrain: %0x\n", read_c0_pagegrain()); 3962306a36Sopenharmony_ci if (cpu_has_htw) { 4062306a36Sopenharmony_ci pr_info("PWField : %0*lx\n", field, read_c0_pwfield()); 4162306a36Sopenharmony_ci pr_info("PWSize : %0*lx\n", field, read_c0_pwsize()); 4262306a36Sopenharmony_ci pr_info("PWCtl : %0x\n", read_c0_pwctl()); 4362306a36Sopenharmony_ci } 4462306a36Sopenharmony_ci} 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistatic inline const char *msk2str(unsigned int mask) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci switch (mask) { 4962306a36Sopenharmony_ci case PM_4K: return "4kb"; 5062306a36Sopenharmony_ci case PM_16K: return "16kb"; 5162306a36Sopenharmony_ci case PM_64K: return "64kb"; 5262306a36Sopenharmony_ci case PM_256K: return "256kb"; 5362306a36Sopenharmony_ci#ifdef CONFIG_CPU_CAVIUM_OCTEON 5462306a36Sopenharmony_ci case PM_8K: return "8kb"; 5562306a36Sopenharmony_ci case PM_32K: return "32kb"; 5662306a36Sopenharmony_ci case PM_128K: return "128kb"; 5762306a36Sopenharmony_ci case PM_512K: return "512kb"; 5862306a36Sopenharmony_ci case PM_2M: return "2Mb"; 5962306a36Sopenharmony_ci case PM_8M: return "8Mb"; 6062306a36Sopenharmony_ci case PM_32M: return "32Mb"; 6162306a36Sopenharmony_ci#endif 6262306a36Sopenharmony_ci } 6362306a36Sopenharmony_ci return ""; 6462306a36Sopenharmony_ci} 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistatic void dump_tlb(int first, int last) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci unsigned long s_entryhi, entryhi, asid, mmid; 6962306a36Sopenharmony_ci unsigned long long entrylo0, entrylo1, pa; 7062306a36Sopenharmony_ci unsigned int s_index, s_pagemask, s_guestctl1 = 0; 7162306a36Sopenharmony_ci unsigned int pagemask, guestctl1 = 0, c0, c1, i; 7262306a36Sopenharmony_ci unsigned long asidmask = cpu_asid_mask(¤t_cpu_data); 7362306a36Sopenharmony_ci int asidwidth = DIV_ROUND_UP(ilog2(asidmask) + 1, 4); 7462306a36Sopenharmony_ci unsigned long s_mmid; 7562306a36Sopenharmony_ci#ifdef CONFIG_32BIT 7662306a36Sopenharmony_ci bool xpa = cpu_has_xpa && (read_c0_pagegrain() & PG_ELPA); 7762306a36Sopenharmony_ci int pwidth = xpa ? 11 : 8; 7862306a36Sopenharmony_ci int vwidth = 8; 7962306a36Sopenharmony_ci#else 8062306a36Sopenharmony_ci bool xpa = false; 8162306a36Sopenharmony_ci int pwidth = 11; 8262306a36Sopenharmony_ci int vwidth = 11; 8362306a36Sopenharmony_ci#endif 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci s_pagemask = read_c0_pagemask(); 8662306a36Sopenharmony_ci s_entryhi = read_c0_entryhi(); 8762306a36Sopenharmony_ci s_index = read_c0_index(); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci if (cpu_has_mmid) 9062306a36Sopenharmony_ci asid = s_mmid = read_c0_memorymapid(); 9162306a36Sopenharmony_ci else 9262306a36Sopenharmony_ci asid = s_entryhi & asidmask; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci if (cpu_has_guestid) 9562306a36Sopenharmony_ci s_guestctl1 = read_c0_guestctl1(); 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci for (i = first; i <= last; i++) { 9862306a36Sopenharmony_ci write_c0_index(i); 9962306a36Sopenharmony_ci mtc0_tlbr_hazard(); 10062306a36Sopenharmony_ci tlb_read(); 10162306a36Sopenharmony_ci tlb_read_hazard(); 10262306a36Sopenharmony_ci pagemask = read_c0_pagemask(); 10362306a36Sopenharmony_ci entryhi = read_c0_entryhi(); 10462306a36Sopenharmony_ci entrylo0 = read_c0_entrylo0(); 10562306a36Sopenharmony_ci entrylo1 = read_c0_entrylo1(); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci if (cpu_has_mmid) 10862306a36Sopenharmony_ci mmid = read_c0_memorymapid(); 10962306a36Sopenharmony_ci else 11062306a36Sopenharmony_ci mmid = entryhi & asidmask; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci if (cpu_has_guestid) 11362306a36Sopenharmony_ci guestctl1 = read_c0_guestctl1(); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci /* EHINV bit marks entire entry as invalid */ 11662306a36Sopenharmony_ci if (cpu_has_tlbinv && entryhi & MIPS_ENTRYHI_EHINV) 11762306a36Sopenharmony_ci continue; 11862306a36Sopenharmony_ci /* 11962306a36Sopenharmony_ci * Prior to tlbinv, unused entries have a virtual address of 12062306a36Sopenharmony_ci * CKSEG0. 12162306a36Sopenharmony_ci */ 12262306a36Sopenharmony_ci if ((entryhi & ~0x1ffffUL) == CKSEG0) 12362306a36Sopenharmony_ci continue; 12462306a36Sopenharmony_ci /* 12562306a36Sopenharmony_ci * ASID takes effect in absence of G (global) bit. 12662306a36Sopenharmony_ci * We check both G bits, even though architecturally they should 12762306a36Sopenharmony_ci * match one another, because some revisions of the SB1 core may 12862306a36Sopenharmony_ci * leave only a single G bit set after a machine check exception 12962306a36Sopenharmony_ci * due to duplicate TLB entry. 13062306a36Sopenharmony_ci */ 13162306a36Sopenharmony_ci if (!((entrylo0 | entrylo1) & ENTRYLO_G) && (mmid != asid)) 13262306a36Sopenharmony_ci continue; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci /* 13562306a36Sopenharmony_ci * Only print entries in use 13662306a36Sopenharmony_ci */ 13762306a36Sopenharmony_ci printk("Index: %2d pgmask=%s ", i, msk2str(pagemask)); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci c0 = (entrylo0 & ENTRYLO_C) >> ENTRYLO_C_SHIFT; 14062306a36Sopenharmony_ci c1 = (entrylo1 & ENTRYLO_C) >> ENTRYLO_C_SHIFT; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci pr_cont("va=%0*lx asid=%0*lx", 14362306a36Sopenharmony_ci vwidth, (entryhi & ~0x1fffUL), 14462306a36Sopenharmony_ci asidwidth, mmid); 14562306a36Sopenharmony_ci if (cpu_has_guestid) 14662306a36Sopenharmony_ci pr_cont(" gid=%02lx", 14762306a36Sopenharmony_ci (guestctl1 & MIPS_GCTL1_RID) 14862306a36Sopenharmony_ci >> MIPS_GCTL1_RID_SHIFT); 14962306a36Sopenharmony_ci /* RI/XI are in awkward places, so mask them off separately */ 15062306a36Sopenharmony_ci pa = entrylo0 & ~(MIPS_ENTRYLO_RI | MIPS_ENTRYLO_XI); 15162306a36Sopenharmony_ci if (xpa) 15262306a36Sopenharmony_ci pa |= (unsigned long long)readx_c0_entrylo0() << 30; 15362306a36Sopenharmony_ci pa = (pa << 6) & PAGE_MASK; 15462306a36Sopenharmony_ci pr_cont("\n\t["); 15562306a36Sopenharmony_ci if (cpu_has_rixi) 15662306a36Sopenharmony_ci pr_cont("ri=%d xi=%d ", 15762306a36Sopenharmony_ci (entrylo0 & MIPS_ENTRYLO_RI) ? 1 : 0, 15862306a36Sopenharmony_ci (entrylo0 & MIPS_ENTRYLO_XI) ? 1 : 0); 15962306a36Sopenharmony_ci pr_cont("pa=%0*llx c=%d d=%d v=%d g=%d] [", 16062306a36Sopenharmony_ci pwidth, pa, c0, 16162306a36Sopenharmony_ci (entrylo0 & ENTRYLO_D) ? 1 : 0, 16262306a36Sopenharmony_ci (entrylo0 & ENTRYLO_V) ? 1 : 0, 16362306a36Sopenharmony_ci (entrylo0 & ENTRYLO_G) ? 1 : 0); 16462306a36Sopenharmony_ci /* RI/XI are in awkward places, so mask them off separately */ 16562306a36Sopenharmony_ci pa = entrylo1 & ~(MIPS_ENTRYLO_RI | MIPS_ENTRYLO_XI); 16662306a36Sopenharmony_ci if (xpa) 16762306a36Sopenharmony_ci pa |= (unsigned long long)readx_c0_entrylo1() << 30; 16862306a36Sopenharmony_ci pa = (pa << 6) & PAGE_MASK; 16962306a36Sopenharmony_ci if (cpu_has_rixi) 17062306a36Sopenharmony_ci pr_cont("ri=%d xi=%d ", 17162306a36Sopenharmony_ci (entrylo1 & MIPS_ENTRYLO_RI) ? 1 : 0, 17262306a36Sopenharmony_ci (entrylo1 & MIPS_ENTRYLO_XI) ? 1 : 0); 17362306a36Sopenharmony_ci pr_cont("pa=%0*llx c=%d d=%d v=%d g=%d]\n", 17462306a36Sopenharmony_ci pwidth, pa, c1, 17562306a36Sopenharmony_ci (entrylo1 & ENTRYLO_D) ? 1 : 0, 17662306a36Sopenharmony_ci (entrylo1 & ENTRYLO_V) ? 1 : 0, 17762306a36Sopenharmony_ci (entrylo1 & ENTRYLO_G) ? 1 : 0); 17862306a36Sopenharmony_ci } 17962306a36Sopenharmony_ci printk("\n"); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci write_c0_entryhi(s_entryhi); 18262306a36Sopenharmony_ci write_c0_index(s_index); 18362306a36Sopenharmony_ci write_c0_pagemask(s_pagemask); 18462306a36Sopenharmony_ci if (cpu_has_guestid) 18562306a36Sopenharmony_ci write_c0_guestctl1(s_guestctl1); 18662306a36Sopenharmony_ci} 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_civoid dump_tlb_all(void) 18962306a36Sopenharmony_ci{ 19062306a36Sopenharmony_ci dump_tlb(0, current_cpu_data.tlbsize - 1); 19162306a36Sopenharmony_ci} 192