1d7d5b05fSDeng-Cheng Zhu /* 2d7d5b05fSDeng-Cheng Zhu * This file is subject to the terms and conditions of the GNU General Public 3d7d5b05fSDeng-Cheng Zhu * License. See the file "COPYING" in the main directory of this archive 4d7d5b05fSDeng-Cheng Zhu * for more details. 5d7d5b05fSDeng-Cheng Zhu * 6d7d5b05fSDeng-Cheng Zhu * KVM/MIPS TLB handling, this file is part of the Linux host kernel so that 7d7d5b05fSDeng-Cheng Zhu * TLB handlers run from KSEG0 8d7d5b05fSDeng-Cheng Zhu * 9d7d5b05fSDeng-Cheng Zhu * Copyright (C) 2012 MIPS Technologies, Inc. All rights reserved. 10d7d5b05fSDeng-Cheng Zhu * Authors: Sanjay Lal <sanjayl@kymasys.com> 11d7d5b05fSDeng-Cheng Zhu */ 12d7d5b05fSDeng-Cheng Zhu 13d7d5b05fSDeng-Cheng Zhu #include <linux/sched.h> 14d7d5b05fSDeng-Cheng Zhu #include <linux/smp.h> 15d7d5b05fSDeng-Cheng Zhu #include <linux/mm.h> 16d7d5b05fSDeng-Cheng Zhu #include <linux/delay.h> 17*403015b3SJames Hogan #include <linux/export.h> 18d7d5b05fSDeng-Cheng Zhu #include <linux/kvm_host.h> 19d7d5b05fSDeng-Cheng Zhu #include <linux/srcu.h> 20d7d5b05fSDeng-Cheng Zhu 21d7d5b05fSDeng-Cheng Zhu #include <asm/cpu.h> 22d7d5b05fSDeng-Cheng Zhu #include <asm/bootinfo.h> 23d7d5b05fSDeng-Cheng Zhu #include <asm/mmu_context.h> 24d7d5b05fSDeng-Cheng Zhu #include <asm/pgtable.h> 25d7d5b05fSDeng-Cheng Zhu #include <asm/cacheflush.h> 26d7d5b05fSDeng-Cheng Zhu #include <asm/tlb.h> 27d7d5b05fSDeng-Cheng Zhu 28d7d5b05fSDeng-Cheng Zhu #undef CONFIG_MIPS_MT 29d7d5b05fSDeng-Cheng Zhu #include <asm/r4kcache.h> 30d7d5b05fSDeng-Cheng Zhu #define CONFIG_MIPS_MT 31d7d5b05fSDeng-Cheng Zhu 32d7d5b05fSDeng-Cheng Zhu #define KVM_GUEST_PC_TLB 0 33d7d5b05fSDeng-Cheng Zhu #define KVM_GUEST_SP_TLB 1 34d7d5b05fSDeng-Cheng Zhu 35d7d5b05fSDeng-Cheng Zhu atomic_t kvm_mips_instance; 36cb1b447fSJames Hogan EXPORT_SYMBOL_GPL(kvm_mips_instance); 37d7d5b05fSDeng-Cheng Zhu 38d7d5b05fSDeng-Cheng Zhu /* These function pointers are initialized once the KVM module is loaded */ 39ba049e93SDan Williams kvm_pfn_t (*kvm_mips_gfn_to_pfn)(struct kvm *kvm, gfn_t gfn); 40cb1b447fSJames Hogan EXPORT_SYMBOL_GPL(kvm_mips_gfn_to_pfn); 41d7d5b05fSDeng-Cheng Zhu 42ba049e93SDan Williams void (*kvm_mips_release_pfn_clean)(kvm_pfn_t pfn); 43cb1b447fSJames Hogan EXPORT_SYMBOL_GPL(kvm_mips_release_pfn_clean); 44d7d5b05fSDeng-Cheng Zhu 45ba049e93SDan Williams bool (*kvm_mips_is_error_pfn)(kvm_pfn_t pfn); 46cb1b447fSJames Hogan EXPORT_SYMBOL_GPL(kvm_mips_is_error_pfn); 47d7d5b05fSDeng-Cheng Zhu 48*403015b3SJames Hogan static u32 kvm_mips_get_kernel_asid(struct kvm_vcpu *vcpu) 49d7d5b05fSDeng-Cheng Zhu { 504edf00a4SPaul Burton int cpu = smp_processor_id(); 514edf00a4SPaul Burton 524edf00a4SPaul Burton return vcpu->arch.guest_kernel_asid[cpu] & 534edf00a4SPaul Burton cpu_asid_mask(&cpu_data[cpu]); 54d7d5b05fSDeng-Cheng Zhu } 55d7d5b05fSDeng-Cheng Zhu 56*403015b3SJames Hogan static u32 kvm_mips_get_user_asid(struct kvm_vcpu *vcpu) 57d7d5b05fSDeng-Cheng Zhu { 584edf00a4SPaul Burton int cpu = smp_processor_id(); 594edf00a4SPaul Burton 604edf00a4SPaul Burton return vcpu->arch.guest_user_asid[cpu] & 614edf00a4SPaul Burton cpu_asid_mask(&cpu_data[cpu]); 62d7d5b05fSDeng-Cheng Zhu } 63d7d5b05fSDeng-Cheng Zhu 64bdb7ed86SJames Hogan inline u32 kvm_mips_get_commpage_asid(struct kvm_vcpu *vcpu) 65d7d5b05fSDeng-Cheng Zhu { 66d7d5b05fSDeng-Cheng Zhu return vcpu->kvm->arch.commpage_tlb; 67d7d5b05fSDeng-Cheng Zhu } 68d7d5b05fSDeng-Cheng Zhu 69d7d5b05fSDeng-Cheng Zhu /* Structure defining an tlb entry data set. */ 70d7d5b05fSDeng-Cheng Zhu 71d7d5b05fSDeng-Cheng Zhu void kvm_mips_dump_host_tlbs(void) 72d7d5b05fSDeng-Cheng Zhu { 73d7d5b05fSDeng-Cheng Zhu unsigned long old_entryhi; 74d7d5b05fSDeng-Cheng Zhu unsigned long old_pagemask; 75d7d5b05fSDeng-Cheng Zhu struct kvm_mips_tlb tlb; 76d7d5b05fSDeng-Cheng Zhu unsigned long flags; 77d7d5b05fSDeng-Cheng Zhu int i; 78d7d5b05fSDeng-Cheng Zhu 79d7d5b05fSDeng-Cheng Zhu local_irq_save(flags); 80d7d5b05fSDeng-Cheng Zhu 81d7d5b05fSDeng-Cheng Zhu old_entryhi = read_c0_entryhi(); 82d7d5b05fSDeng-Cheng Zhu old_pagemask = read_c0_pagemask(); 83d7d5b05fSDeng-Cheng Zhu 84d7d5b05fSDeng-Cheng Zhu kvm_info("HOST TLBs:\n"); 854edf00a4SPaul Burton kvm_info("ASID: %#lx\n", read_c0_entryhi() & 864edf00a4SPaul Burton cpu_asid_mask(¤t_cpu_data)); 87d7d5b05fSDeng-Cheng Zhu 88d7d5b05fSDeng-Cheng Zhu for (i = 0; i < current_cpu_data.tlbsize; i++) { 89d7d5b05fSDeng-Cheng Zhu write_c0_index(i); 90d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 91d7d5b05fSDeng-Cheng Zhu 92d7d5b05fSDeng-Cheng Zhu tlb_read(); 93d7d5b05fSDeng-Cheng Zhu tlbw_use_hazard(); 94d7d5b05fSDeng-Cheng Zhu 95d7d5b05fSDeng-Cheng Zhu tlb.tlb_hi = read_c0_entryhi(); 96d7d5b05fSDeng-Cheng Zhu tlb.tlb_lo0 = read_c0_entrylo0(); 97d7d5b05fSDeng-Cheng Zhu tlb.tlb_lo1 = read_c0_entrylo1(); 98d7d5b05fSDeng-Cheng Zhu tlb.tlb_mask = read_c0_pagemask(); 99d7d5b05fSDeng-Cheng Zhu 100d7d5b05fSDeng-Cheng Zhu kvm_info("TLB%c%3d Hi 0x%08lx ", 101d7d5b05fSDeng-Cheng Zhu (tlb.tlb_lo0 | tlb.tlb_lo1) & MIPS3_PG_V ? ' ' : '*', 102d7d5b05fSDeng-Cheng Zhu i, tlb.tlb_hi); 1038cffd197SJames Hogan kvm_info("Lo0=0x%09llx %c%c attr %lx ", 1048cffd197SJames Hogan (u64) mips3_tlbpfn_to_paddr(tlb.tlb_lo0), 105d7d5b05fSDeng-Cheng Zhu (tlb.tlb_lo0 & MIPS3_PG_D) ? 'D' : ' ', 106d7d5b05fSDeng-Cheng Zhu (tlb.tlb_lo0 & MIPS3_PG_G) ? 'G' : ' ', 107d7d5b05fSDeng-Cheng Zhu (tlb.tlb_lo0 >> 3) & 7); 1088cffd197SJames Hogan kvm_info("Lo1=0x%09llx %c%c attr %lx sz=%lx\n", 1098cffd197SJames Hogan (u64) mips3_tlbpfn_to_paddr(tlb.tlb_lo1), 110d7d5b05fSDeng-Cheng Zhu (tlb.tlb_lo1 & MIPS3_PG_D) ? 'D' : ' ', 111d7d5b05fSDeng-Cheng Zhu (tlb.tlb_lo1 & MIPS3_PG_G) ? 'G' : ' ', 112d7d5b05fSDeng-Cheng Zhu (tlb.tlb_lo1 >> 3) & 7, tlb.tlb_mask); 113d7d5b05fSDeng-Cheng Zhu } 114d7d5b05fSDeng-Cheng Zhu write_c0_entryhi(old_entryhi); 115d7d5b05fSDeng-Cheng Zhu write_c0_pagemask(old_pagemask); 116d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 117d7d5b05fSDeng-Cheng Zhu local_irq_restore(flags); 118d7d5b05fSDeng-Cheng Zhu } 119cb1b447fSJames Hogan EXPORT_SYMBOL_GPL(kvm_mips_dump_host_tlbs); 120d7d5b05fSDeng-Cheng Zhu 121d7d5b05fSDeng-Cheng Zhu void kvm_mips_dump_guest_tlbs(struct kvm_vcpu *vcpu) 122d7d5b05fSDeng-Cheng Zhu { 123d7d5b05fSDeng-Cheng Zhu struct mips_coproc *cop0 = vcpu->arch.cop0; 124d7d5b05fSDeng-Cheng Zhu struct kvm_mips_tlb tlb; 125d7d5b05fSDeng-Cheng Zhu int i; 126d7d5b05fSDeng-Cheng Zhu 127d7d5b05fSDeng-Cheng Zhu kvm_info("Guest TLBs:\n"); 128d7d5b05fSDeng-Cheng Zhu kvm_info("Guest EntryHi: %#lx\n", kvm_read_c0_guest_entryhi(cop0)); 129d7d5b05fSDeng-Cheng Zhu 130d7d5b05fSDeng-Cheng Zhu for (i = 0; i < KVM_MIPS_GUEST_TLB_SIZE; i++) { 131d7d5b05fSDeng-Cheng Zhu tlb = vcpu->arch.guest_tlb[i]; 132d7d5b05fSDeng-Cheng Zhu kvm_info("TLB%c%3d Hi 0x%08lx ", 133d7d5b05fSDeng-Cheng Zhu (tlb.tlb_lo0 | tlb.tlb_lo1) & MIPS3_PG_V ? ' ' : '*', 134d7d5b05fSDeng-Cheng Zhu i, tlb.tlb_hi); 1358cffd197SJames Hogan kvm_info("Lo0=0x%09llx %c%c attr %lx ", 1368cffd197SJames Hogan (u64) mips3_tlbpfn_to_paddr(tlb.tlb_lo0), 137d7d5b05fSDeng-Cheng Zhu (tlb.tlb_lo0 & MIPS3_PG_D) ? 'D' : ' ', 138d7d5b05fSDeng-Cheng Zhu (tlb.tlb_lo0 & MIPS3_PG_G) ? 'G' : ' ', 139d7d5b05fSDeng-Cheng Zhu (tlb.tlb_lo0 >> 3) & 7); 1408cffd197SJames Hogan kvm_info("Lo1=0x%09llx %c%c attr %lx sz=%lx\n", 1418cffd197SJames Hogan (u64) mips3_tlbpfn_to_paddr(tlb.tlb_lo1), 142d7d5b05fSDeng-Cheng Zhu (tlb.tlb_lo1 & MIPS3_PG_D) ? 'D' : ' ', 143d7d5b05fSDeng-Cheng Zhu (tlb.tlb_lo1 & MIPS3_PG_G) ? 'G' : ' ', 144d7d5b05fSDeng-Cheng Zhu (tlb.tlb_lo1 >> 3) & 7, tlb.tlb_mask); 145d7d5b05fSDeng-Cheng Zhu } 146d7d5b05fSDeng-Cheng Zhu } 147cb1b447fSJames Hogan EXPORT_SYMBOL_GPL(kvm_mips_dump_guest_tlbs); 148d7d5b05fSDeng-Cheng Zhu 149d7d5b05fSDeng-Cheng Zhu /* XXXKYMA: Must be called with interrupts disabled */ 150d7d5b05fSDeng-Cheng Zhu /* set flush_dcache_mask == 0 if no dcache flush required */ 151d7d5b05fSDeng-Cheng Zhu int kvm_mips_host_tlb_write(struct kvm_vcpu *vcpu, unsigned long entryhi, 152d7d5b05fSDeng-Cheng Zhu unsigned long entrylo0, unsigned long entrylo1, 153d7d5b05fSDeng-Cheng Zhu int flush_dcache_mask) 154d7d5b05fSDeng-Cheng Zhu { 155d7d5b05fSDeng-Cheng Zhu unsigned long flags; 156d7d5b05fSDeng-Cheng Zhu unsigned long old_entryhi; 157d7d5b05fSDeng-Cheng Zhu int idx; 158d7d5b05fSDeng-Cheng Zhu 159d7d5b05fSDeng-Cheng Zhu local_irq_save(flags); 160d7d5b05fSDeng-Cheng Zhu 161d7d5b05fSDeng-Cheng Zhu old_entryhi = read_c0_entryhi(); 162d7d5b05fSDeng-Cheng Zhu write_c0_entryhi(entryhi); 163d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 164d7d5b05fSDeng-Cheng Zhu 165d7d5b05fSDeng-Cheng Zhu tlb_probe(); 166d7d5b05fSDeng-Cheng Zhu tlb_probe_hazard(); 167d7d5b05fSDeng-Cheng Zhu idx = read_c0_index(); 168d7d5b05fSDeng-Cheng Zhu 169d7d5b05fSDeng-Cheng Zhu if (idx > current_cpu_data.tlbsize) { 170d7d5b05fSDeng-Cheng Zhu kvm_err("%s: Invalid Index: %d\n", __func__, idx); 171d7d5b05fSDeng-Cheng Zhu kvm_mips_dump_host_tlbs(); 172cfec0e75STapasweni Pathak local_irq_restore(flags); 173d7d5b05fSDeng-Cheng Zhu return -1; 174d7d5b05fSDeng-Cheng Zhu } 175d7d5b05fSDeng-Cheng Zhu 176d7d5b05fSDeng-Cheng Zhu write_c0_entrylo0(entrylo0); 177d7d5b05fSDeng-Cheng Zhu write_c0_entrylo1(entrylo1); 178d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 179d7d5b05fSDeng-Cheng Zhu 180d7d5b05fSDeng-Cheng Zhu if (idx < 0) 181d7d5b05fSDeng-Cheng Zhu tlb_write_random(); 182d7d5b05fSDeng-Cheng Zhu else 183d7d5b05fSDeng-Cheng Zhu tlb_write_indexed(); 184d7d5b05fSDeng-Cheng Zhu tlbw_use_hazard(); 185d7d5b05fSDeng-Cheng Zhu 186d7d5b05fSDeng-Cheng Zhu kvm_debug("@ %#lx idx: %2d [entryhi(R): %#lx] entrylo0(R): 0x%08lx, entrylo1(R): 0x%08lx\n", 187d7d5b05fSDeng-Cheng Zhu vcpu->arch.pc, idx, read_c0_entryhi(), 188d7d5b05fSDeng-Cheng Zhu read_c0_entrylo0(), read_c0_entrylo1()); 189d7d5b05fSDeng-Cheng Zhu 190d7d5b05fSDeng-Cheng Zhu /* Flush D-cache */ 191d7d5b05fSDeng-Cheng Zhu if (flush_dcache_mask) { 192d7d5b05fSDeng-Cheng Zhu if (entrylo0 & MIPS3_PG_V) { 193d7d5b05fSDeng-Cheng Zhu ++vcpu->stat.flush_dcache_exits; 194d7d5b05fSDeng-Cheng Zhu flush_data_cache_page((entryhi & VPN2_MASK) & 195d7d5b05fSDeng-Cheng Zhu ~flush_dcache_mask); 196d7d5b05fSDeng-Cheng Zhu } 197d7d5b05fSDeng-Cheng Zhu if (entrylo1 & MIPS3_PG_V) { 198d7d5b05fSDeng-Cheng Zhu ++vcpu->stat.flush_dcache_exits; 199d7d5b05fSDeng-Cheng Zhu flush_data_cache_page(((entryhi & VPN2_MASK) & 200d7d5b05fSDeng-Cheng Zhu ~flush_dcache_mask) | 201d7d5b05fSDeng-Cheng Zhu (0x1 << PAGE_SHIFT)); 202d7d5b05fSDeng-Cheng Zhu } 203d7d5b05fSDeng-Cheng Zhu } 204d7d5b05fSDeng-Cheng Zhu 205d7d5b05fSDeng-Cheng Zhu /* Restore old ASID */ 206d7d5b05fSDeng-Cheng Zhu write_c0_entryhi(old_entryhi); 207d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 208d7d5b05fSDeng-Cheng Zhu tlbw_use_hazard(); 209d7d5b05fSDeng-Cheng Zhu local_irq_restore(flags); 210d7d5b05fSDeng-Cheng Zhu return 0; 211d7d5b05fSDeng-Cheng Zhu } 212*403015b3SJames Hogan EXPORT_SYMBOL_GPL(kvm_mips_host_tlb_write); 213d7d5b05fSDeng-Cheng Zhu 214d7d5b05fSDeng-Cheng Zhu int kvm_mips_handle_commpage_tlb_fault(unsigned long badvaddr, 215d7d5b05fSDeng-Cheng Zhu struct kvm_vcpu *vcpu) 216d7d5b05fSDeng-Cheng Zhu { 217ba049e93SDan Williams kvm_pfn_t pfn0, pfn1; 218d7d5b05fSDeng-Cheng Zhu unsigned long flags, old_entryhi = 0, vaddr = 0; 219d7d5b05fSDeng-Cheng Zhu unsigned long entrylo0 = 0, entrylo1 = 0; 220d7d5b05fSDeng-Cheng Zhu 221d7d5b05fSDeng-Cheng Zhu pfn0 = CPHYSADDR(vcpu->arch.kseg0_commpage) >> PAGE_SHIFT; 222d7d5b05fSDeng-Cheng Zhu pfn1 = 0; 223d7d5b05fSDeng-Cheng Zhu entrylo0 = mips3_paddr_to_tlbpfn(pfn0 << PAGE_SHIFT) | (0x3 << 3) | 224d7d5b05fSDeng-Cheng Zhu (1 << 2) | (0x1 << 1); 225d7d5b05fSDeng-Cheng Zhu entrylo1 = 0; 226d7d5b05fSDeng-Cheng Zhu 227d7d5b05fSDeng-Cheng Zhu local_irq_save(flags); 228d7d5b05fSDeng-Cheng Zhu 229d7d5b05fSDeng-Cheng Zhu old_entryhi = read_c0_entryhi(); 230d7d5b05fSDeng-Cheng Zhu vaddr = badvaddr & (PAGE_MASK << 1); 231d7d5b05fSDeng-Cheng Zhu write_c0_entryhi(vaddr | kvm_mips_get_kernel_asid(vcpu)); 232d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 233d7d5b05fSDeng-Cheng Zhu write_c0_entrylo0(entrylo0); 234d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 235d7d5b05fSDeng-Cheng Zhu write_c0_entrylo1(entrylo1); 236d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 237d7d5b05fSDeng-Cheng Zhu write_c0_index(kvm_mips_get_commpage_asid(vcpu)); 238d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 239d7d5b05fSDeng-Cheng Zhu tlb_write_indexed(); 240d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 241d7d5b05fSDeng-Cheng Zhu tlbw_use_hazard(); 242d7d5b05fSDeng-Cheng Zhu 243d7d5b05fSDeng-Cheng Zhu kvm_debug("@ %#lx idx: %2d [entryhi(R): %#lx] entrylo0 (R): 0x%08lx, entrylo1(R): 0x%08lx\n", 244d7d5b05fSDeng-Cheng Zhu vcpu->arch.pc, read_c0_index(), read_c0_entryhi(), 245d7d5b05fSDeng-Cheng Zhu read_c0_entrylo0(), read_c0_entrylo1()); 246d7d5b05fSDeng-Cheng Zhu 247d7d5b05fSDeng-Cheng Zhu /* Restore old ASID */ 248d7d5b05fSDeng-Cheng Zhu write_c0_entryhi(old_entryhi); 249d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 250d7d5b05fSDeng-Cheng Zhu tlbw_use_hazard(); 251d7d5b05fSDeng-Cheng Zhu local_irq_restore(flags); 252d7d5b05fSDeng-Cheng Zhu 253d7d5b05fSDeng-Cheng Zhu return 0; 254d7d5b05fSDeng-Cheng Zhu } 255cb1b447fSJames Hogan EXPORT_SYMBOL_GPL(kvm_mips_handle_commpage_tlb_fault); 256d7d5b05fSDeng-Cheng Zhu 257d7d5b05fSDeng-Cheng Zhu int kvm_mips_guest_tlb_lookup(struct kvm_vcpu *vcpu, unsigned long entryhi) 258d7d5b05fSDeng-Cheng Zhu { 259d7d5b05fSDeng-Cheng Zhu int i; 260d7d5b05fSDeng-Cheng Zhu int index = -1; 261d7d5b05fSDeng-Cheng Zhu struct kvm_mips_tlb *tlb = vcpu->arch.guest_tlb; 262d7d5b05fSDeng-Cheng Zhu 263d7d5b05fSDeng-Cheng Zhu for (i = 0; i < KVM_MIPS_GUEST_TLB_SIZE; i++) { 264d7d5b05fSDeng-Cheng Zhu if (TLB_HI_VPN2_HIT(tlb[i], entryhi) && 265d7d5b05fSDeng-Cheng Zhu TLB_HI_ASID_HIT(tlb[i], entryhi)) { 266d7d5b05fSDeng-Cheng Zhu index = i; 267d7d5b05fSDeng-Cheng Zhu break; 268d7d5b05fSDeng-Cheng Zhu } 269d7d5b05fSDeng-Cheng Zhu } 270d7d5b05fSDeng-Cheng Zhu 271d7d5b05fSDeng-Cheng Zhu kvm_debug("%s: entryhi: %#lx, index: %d lo0: %#lx, lo1: %#lx\n", 272d7d5b05fSDeng-Cheng Zhu __func__, entryhi, index, tlb[i].tlb_lo0, tlb[i].tlb_lo1); 273d7d5b05fSDeng-Cheng Zhu 274d7d5b05fSDeng-Cheng Zhu return index; 275d7d5b05fSDeng-Cheng Zhu } 276cb1b447fSJames Hogan EXPORT_SYMBOL_GPL(kvm_mips_guest_tlb_lookup); 277d7d5b05fSDeng-Cheng Zhu 278d7d5b05fSDeng-Cheng Zhu int kvm_mips_host_tlb_lookup(struct kvm_vcpu *vcpu, unsigned long vaddr) 279d7d5b05fSDeng-Cheng Zhu { 280d7d5b05fSDeng-Cheng Zhu unsigned long old_entryhi, flags; 281d7d5b05fSDeng-Cheng Zhu int idx; 282d7d5b05fSDeng-Cheng Zhu 283d7d5b05fSDeng-Cheng Zhu local_irq_save(flags); 284d7d5b05fSDeng-Cheng Zhu 285d7d5b05fSDeng-Cheng Zhu old_entryhi = read_c0_entryhi(); 286d7d5b05fSDeng-Cheng Zhu 287d7d5b05fSDeng-Cheng Zhu if (KVM_GUEST_KERNEL_MODE(vcpu)) 288d7d5b05fSDeng-Cheng Zhu write_c0_entryhi((vaddr & VPN2_MASK) | 289d7d5b05fSDeng-Cheng Zhu kvm_mips_get_kernel_asid(vcpu)); 290d7d5b05fSDeng-Cheng Zhu else { 291d7d5b05fSDeng-Cheng Zhu write_c0_entryhi((vaddr & VPN2_MASK) | 292d7d5b05fSDeng-Cheng Zhu kvm_mips_get_user_asid(vcpu)); 293d7d5b05fSDeng-Cheng Zhu } 294d7d5b05fSDeng-Cheng Zhu 295d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 296d7d5b05fSDeng-Cheng Zhu 297d7d5b05fSDeng-Cheng Zhu tlb_probe(); 298d7d5b05fSDeng-Cheng Zhu tlb_probe_hazard(); 299d7d5b05fSDeng-Cheng Zhu idx = read_c0_index(); 300d7d5b05fSDeng-Cheng Zhu 301d7d5b05fSDeng-Cheng Zhu /* Restore old ASID */ 302d7d5b05fSDeng-Cheng Zhu write_c0_entryhi(old_entryhi); 303d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 304d7d5b05fSDeng-Cheng Zhu tlbw_use_hazard(); 305d7d5b05fSDeng-Cheng Zhu 306d7d5b05fSDeng-Cheng Zhu local_irq_restore(flags); 307d7d5b05fSDeng-Cheng Zhu 308d7d5b05fSDeng-Cheng Zhu kvm_debug("Host TLB lookup, %#lx, idx: %2d\n", vaddr, idx); 309d7d5b05fSDeng-Cheng Zhu 310d7d5b05fSDeng-Cheng Zhu return idx; 311d7d5b05fSDeng-Cheng Zhu } 312cb1b447fSJames Hogan EXPORT_SYMBOL_GPL(kvm_mips_host_tlb_lookup); 313d7d5b05fSDeng-Cheng Zhu 314d7d5b05fSDeng-Cheng Zhu int kvm_mips_host_tlb_inv(struct kvm_vcpu *vcpu, unsigned long va) 315d7d5b05fSDeng-Cheng Zhu { 316d7d5b05fSDeng-Cheng Zhu int idx; 317d7d5b05fSDeng-Cheng Zhu unsigned long flags, old_entryhi; 318d7d5b05fSDeng-Cheng Zhu 319d7d5b05fSDeng-Cheng Zhu local_irq_save(flags); 320d7d5b05fSDeng-Cheng Zhu 321d7d5b05fSDeng-Cheng Zhu old_entryhi = read_c0_entryhi(); 322d7d5b05fSDeng-Cheng Zhu 323d7d5b05fSDeng-Cheng Zhu write_c0_entryhi((va & VPN2_MASK) | kvm_mips_get_user_asid(vcpu)); 324d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 325d7d5b05fSDeng-Cheng Zhu 326d7d5b05fSDeng-Cheng Zhu tlb_probe(); 327d7d5b05fSDeng-Cheng Zhu tlb_probe_hazard(); 328d7d5b05fSDeng-Cheng Zhu idx = read_c0_index(); 329d7d5b05fSDeng-Cheng Zhu 330d7d5b05fSDeng-Cheng Zhu if (idx >= current_cpu_data.tlbsize) 331d7d5b05fSDeng-Cheng Zhu BUG(); 332d7d5b05fSDeng-Cheng Zhu 333d7d5b05fSDeng-Cheng Zhu if (idx > 0) { 334d7d5b05fSDeng-Cheng Zhu write_c0_entryhi(UNIQUE_ENTRYHI(idx)); 335d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 336d7d5b05fSDeng-Cheng Zhu 337d7d5b05fSDeng-Cheng Zhu write_c0_entrylo0(0); 338d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 339d7d5b05fSDeng-Cheng Zhu 340d7d5b05fSDeng-Cheng Zhu write_c0_entrylo1(0); 341d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 342d7d5b05fSDeng-Cheng Zhu 343d7d5b05fSDeng-Cheng Zhu tlb_write_indexed(); 344d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 345d7d5b05fSDeng-Cheng Zhu } 346d7d5b05fSDeng-Cheng Zhu 347d7d5b05fSDeng-Cheng Zhu write_c0_entryhi(old_entryhi); 348d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 349d7d5b05fSDeng-Cheng Zhu tlbw_use_hazard(); 350d7d5b05fSDeng-Cheng Zhu 351d7d5b05fSDeng-Cheng Zhu local_irq_restore(flags); 352d7d5b05fSDeng-Cheng Zhu 353d7d5b05fSDeng-Cheng Zhu if (idx > 0) 354d7d5b05fSDeng-Cheng Zhu kvm_debug("%s: Invalidated entryhi %#lx @ idx %d\n", __func__, 355d7d5b05fSDeng-Cheng Zhu (va & VPN2_MASK) | kvm_mips_get_user_asid(vcpu), idx); 356d7d5b05fSDeng-Cheng Zhu 357d7d5b05fSDeng-Cheng Zhu return 0; 358d7d5b05fSDeng-Cheng Zhu } 359cb1b447fSJames Hogan EXPORT_SYMBOL_GPL(kvm_mips_host_tlb_inv); 360d7d5b05fSDeng-Cheng Zhu 361d7d5b05fSDeng-Cheng Zhu void kvm_mips_flush_host_tlb(int skip_kseg0) 362d7d5b05fSDeng-Cheng Zhu { 363d7d5b05fSDeng-Cheng Zhu unsigned long flags; 364d7d5b05fSDeng-Cheng Zhu unsigned long old_entryhi, entryhi; 365d7d5b05fSDeng-Cheng Zhu unsigned long old_pagemask; 366d7d5b05fSDeng-Cheng Zhu int entry = 0; 367d7d5b05fSDeng-Cheng Zhu int maxentry = current_cpu_data.tlbsize; 368d7d5b05fSDeng-Cheng Zhu 369d7d5b05fSDeng-Cheng Zhu local_irq_save(flags); 370d7d5b05fSDeng-Cheng Zhu 371d7d5b05fSDeng-Cheng Zhu old_entryhi = read_c0_entryhi(); 372d7d5b05fSDeng-Cheng Zhu old_pagemask = read_c0_pagemask(); 373d7d5b05fSDeng-Cheng Zhu 374d7d5b05fSDeng-Cheng Zhu /* Blast 'em all away. */ 375d7d5b05fSDeng-Cheng Zhu for (entry = 0; entry < maxentry; entry++) { 376d7d5b05fSDeng-Cheng Zhu write_c0_index(entry); 377d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 378d7d5b05fSDeng-Cheng Zhu 379d7d5b05fSDeng-Cheng Zhu if (skip_kseg0) { 380d7d5b05fSDeng-Cheng Zhu tlb_read(); 381d7d5b05fSDeng-Cheng Zhu tlbw_use_hazard(); 382d7d5b05fSDeng-Cheng Zhu 383d7d5b05fSDeng-Cheng Zhu entryhi = read_c0_entryhi(); 384d7d5b05fSDeng-Cheng Zhu 385d7d5b05fSDeng-Cheng Zhu /* Don't blow away guest kernel entries */ 386d7d5b05fSDeng-Cheng Zhu if (KVM_GUEST_KSEGX(entryhi) == KVM_GUEST_KSEG0) 387d7d5b05fSDeng-Cheng Zhu continue; 388d7d5b05fSDeng-Cheng Zhu } 389d7d5b05fSDeng-Cheng Zhu 390d7d5b05fSDeng-Cheng Zhu /* Make sure all entries differ. */ 391d7d5b05fSDeng-Cheng Zhu write_c0_entryhi(UNIQUE_ENTRYHI(entry)); 392d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 393d7d5b05fSDeng-Cheng Zhu write_c0_entrylo0(0); 394d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 395d7d5b05fSDeng-Cheng Zhu write_c0_entrylo1(0); 396d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 397d7d5b05fSDeng-Cheng Zhu 398d7d5b05fSDeng-Cheng Zhu tlb_write_indexed(); 399d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 400d7d5b05fSDeng-Cheng Zhu } 401d7d5b05fSDeng-Cheng Zhu 402d7d5b05fSDeng-Cheng Zhu tlbw_use_hazard(); 403d7d5b05fSDeng-Cheng Zhu 404d7d5b05fSDeng-Cheng Zhu write_c0_entryhi(old_entryhi); 405d7d5b05fSDeng-Cheng Zhu write_c0_pagemask(old_pagemask); 406d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 407d7d5b05fSDeng-Cheng Zhu tlbw_use_hazard(); 408d7d5b05fSDeng-Cheng Zhu 409d7d5b05fSDeng-Cheng Zhu local_irq_restore(flags); 410d7d5b05fSDeng-Cheng Zhu } 411cb1b447fSJames Hogan EXPORT_SYMBOL_GPL(kvm_mips_flush_host_tlb); 412d7d5b05fSDeng-Cheng Zhu 413d7d5b05fSDeng-Cheng Zhu void kvm_local_flush_tlb_all(void) 414d7d5b05fSDeng-Cheng Zhu { 415d7d5b05fSDeng-Cheng Zhu unsigned long flags; 416d7d5b05fSDeng-Cheng Zhu unsigned long old_ctx; 417d7d5b05fSDeng-Cheng Zhu int entry = 0; 418d7d5b05fSDeng-Cheng Zhu 419d7d5b05fSDeng-Cheng Zhu local_irq_save(flags); 420d7d5b05fSDeng-Cheng Zhu /* Save old context and create impossible VPN2 value */ 421d7d5b05fSDeng-Cheng Zhu old_ctx = read_c0_entryhi(); 422d7d5b05fSDeng-Cheng Zhu write_c0_entrylo0(0); 423d7d5b05fSDeng-Cheng Zhu write_c0_entrylo1(0); 424d7d5b05fSDeng-Cheng Zhu 425d7d5b05fSDeng-Cheng Zhu /* Blast 'em all away. */ 426d7d5b05fSDeng-Cheng Zhu while (entry < current_cpu_data.tlbsize) { 427d7d5b05fSDeng-Cheng Zhu /* Make sure all entries differ. */ 428d7d5b05fSDeng-Cheng Zhu write_c0_entryhi(UNIQUE_ENTRYHI(entry)); 429d7d5b05fSDeng-Cheng Zhu write_c0_index(entry); 430d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 431d7d5b05fSDeng-Cheng Zhu tlb_write_indexed(); 432d7d5b05fSDeng-Cheng Zhu entry++; 433d7d5b05fSDeng-Cheng Zhu } 434d7d5b05fSDeng-Cheng Zhu tlbw_use_hazard(); 435d7d5b05fSDeng-Cheng Zhu write_c0_entryhi(old_ctx); 436d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 437d7d5b05fSDeng-Cheng Zhu 438d7d5b05fSDeng-Cheng Zhu local_irq_restore(flags); 439d7d5b05fSDeng-Cheng Zhu } 440cb1b447fSJames Hogan EXPORT_SYMBOL_GPL(kvm_local_flush_tlb_all); 441