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> 17403015b3SJames 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> 27e922a4cbSJames Hogan #include <asm/tlbdebug.h> 28d7d5b05fSDeng-Cheng Zhu 29d7d5b05fSDeng-Cheng Zhu #undef CONFIG_MIPS_MT 30d7d5b05fSDeng-Cheng Zhu #include <asm/r4kcache.h> 31d7d5b05fSDeng-Cheng Zhu #define CONFIG_MIPS_MT 32d7d5b05fSDeng-Cheng Zhu 33d7d5b05fSDeng-Cheng Zhu #define KVM_GUEST_PC_TLB 0 34d7d5b05fSDeng-Cheng Zhu #define KVM_GUEST_SP_TLB 1 35d7d5b05fSDeng-Cheng Zhu 36d7d5b05fSDeng-Cheng Zhu atomic_t kvm_mips_instance; 37cb1b447fSJames Hogan EXPORT_SYMBOL_GPL(kvm_mips_instance); 38d7d5b05fSDeng-Cheng Zhu 39403015b3SJames Hogan static u32 kvm_mips_get_kernel_asid(struct kvm_vcpu *vcpu) 40d7d5b05fSDeng-Cheng Zhu { 41c550d539SJames Hogan struct mm_struct *kern_mm = &vcpu->arch.guest_kernel_mm; 424edf00a4SPaul Burton int cpu = smp_processor_id(); 434edf00a4SPaul Burton 44c550d539SJames Hogan return cpu_asid(cpu, kern_mm); 45d7d5b05fSDeng-Cheng Zhu } 46d7d5b05fSDeng-Cheng Zhu 47403015b3SJames Hogan static u32 kvm_mips_get_user_asid(struct kvm_vcpu *vcpu) 48d7d5b05fSDeng-Cheng Zhu { 49c550d539SJames Hogan struct mm_struct *user_mm = &vcpu->arch.guest_user_mm; 504edf00a4SPaul Burton int cpu = smp_processor_id(); 514edf00a4SPaul Burton 52c550d539SJames Hogan return cpu_asid(cpu, user_mm); 53d7d5b05fSDeng-Cheng Zhu } 54d7d5b05fSDeng-Cheng Zhu 55bdb7ed86SJames Hogan inline u32 kvm_mips_get_commpage_asid(struct kvm_vcpu *vcpu) 56d7d5b05fSDeng-Cheng Zhu { 57d7d5b05fSDeng-Cheng Zhu return vcpu->kvm->arch.commpage_tlb; 58d7d5b05fSDeng-Cheng Zhu } 59d7d5b05fSDeng-Cheng Zhu 60d7d5b05fSDeng-Cheng Zhu /* Structure defining an tlb entry data set. */ 61d7d5b05fSDeng-Cheng Zhu 62d7d5b05fSDeng-Cheng Zhu void kvm_mips_dump_host_tlbs(void) 63d7d5b05fSDeng-Cheng Zhu { 64d7d5b05fSDeng-Cheng Zhu unsigned long flags; 65d7d5b05fSDeng-Cheng Zhu 66d7d5b05fSDeng-Cheng Zhu local_irq_save(flags); 67d7d5b05fSDeng-Cheng Zhu 68d7d5b05fSDeng-Cheng Zhu kvm_info("HOST TLBs:\n"); 69e922a4cbSJames Hogan dump_tlb_regs(); 70e922a4cbSJames Hogan pr_info("\n"); 71e922a4cbSJames Hogan dump_tlb_all(); 72d7d5b05fSDeng-Cheng Zhu 73d7d5b05fSDeng-Cheng Zhu local_irq_restore(flags); 74d7d5b05fSDeng-Cheng Zhu } 75cb1b447fSJames Hogan EXPORT_SYMBOL_GPL(kvm_mips_dump_host_tlbs); 76d7d5b05fSDeng-Cheng Zhu 77d7d5b05fSDeng-Cheng Zhu void kvm_mips_dump_guest_tlbs(struct kvm_vcpu *vcpu) 78d7d5b05fSDeng-Cheng Zhu { 79d7d5b05fSDeng-Cheng Zhu struct mips_coproc *cop0 = vcpu->arch.cop0; 80d7d5b05fSDeng-Cheng Zhu struct kvm_mips_tlb tlb; 81d7d5b05fSDeng-Cheng Zhu int i; 82d7d5b05fSDeng-Cheng Zhu 83d7d5b05fSDeng-Cheng Zhu kvm_info("Guest TLBs:\n"); 84d7d5b05fSDeng-Cheng Zhu kvm_info("Guest EntryHi: %#lx\n", kvm_read_c0_guest_entryhi(cop0)); 85d7d5b05fSDeng-Cheng Zhu 86d7d5b05fSDeng-Cheng Zhu for (i = 0; i < KVM_MIPS_GUEST_TLB_SIZE; i++) { 87d7d5b05fSDeng-Cheng Zhu tlb = vcpu->arch.guest_tlb[i]; 88d7d5b05fSDeng-Cheng Zhu kvm_info("TLB%c%3d Hi 0x%08lx ", 89e6207bbeSJames Hogan (tlb.tlb_lo[0] | tlb.tlb_lo[1]) & ENTRYLO_V 909fbfb06aSJames Hogan ? ' ' : '*', 91d7d5b05fSDeng-Cheng Zhu i, tlb.tlb_hi); 928cffd197SJames Hogan kvm_info("Lo0=0x%09llx %c%c attr %lx ", 939fbfb06aSJames Hogan (u64) mips3_tlbpfn_to_paddr(tlb.tlb_lo[0]), 94e6207bbeSJames Hogan (tlb.tlb_lo[0] & ENTRYLO_D) ? 'D' : ' ', 95e6207bbeSJames Hogan (tlb.tlb_lo[0] & ENTRYLO_G) ? 'G' : ' ', 96e6207bbeSJames Hogan (tlb.tlb_lo[0] & ENTRYLO_C) >> ENTRYLO_C_SHIFT); 978cffd197SJames Hogan kvm_info("Lo1=0x%09llx %c%c attr %lx sz=%lx\n", 989fbfb06aSJames Hogan (u64) mips3_tlbpfn_to_paddr(tlb.tlb_lo[1]), 99e6207bbeSJames Hogan (tlb.tlb_lo[1] & ENTRYLO_D) ? 'D' : ' ', 100e6207bbeSJames Hogan (tlb.tlb_lo[1] & ENTRYLO_G) ? 'G' : ' ', 101e6207bbeSJames Hogan (tlb.tlb_lo[1] & ENTRYLO_C) >> ENTRYLO_C_SHIFT, 102e6207bbeSJames Hogan tlb.tlb_mask); 103d7d5b05fSDeng-Cheng Zhu } 104d7d5b05fSDeng-Cheng Zhu } 105cb1b447fSJames Hogan EXPORT_SYMBOL_GPL(kvm_mips_dump_guest_tlbs); 106d7d5b05fSDeng-Cheng Zhu 107d7d5b05fSDeng-Cheng Zhu /* XXXKYMA: Must be called with interrupts disabled */ 108d7d5b05fSDeng-Cheng Zhu /* set flush_dcache_mask == 0 if no dcache flush required */ 109d7d5b05fSDeng-Cheng Zhu int kvm_mips_host_tlb_write(struct kvm_vcpu *vcpu, unsigned long entryhi, 110d7d5b05fSDeng-Cheng Zhu unsigned long entrylo0, unsigned long entrylo1, 111d7d5b05fSDeng-Cheng Zhu int flush_dcache_mask) 112d7d5b05fSDeng-Cheng Zhu { 113d7d5b05fSDeng-Cheng Zhu unsigned long flags; 114d7d5b05fSDeng-Cheng Zhu unsigned long old_entryhi; 115d7d5b05fSDeng-Cheng Zhu int idx; 116d7d5b05fSDeng-Cheng Zhu 117d7d5b05fSDeng-Cheng Zhu local_irq_save(flags); 118d7d5b05fSDeng-Cheng Zhu 119d7d5b05fSDeng-Cheng Zhu old_entryhi = read_c0_entryhi(); 120d7d5b05fSDeng-Cheng Zhu write_c0_entryhi(entryhi); 121d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 122d7d5b05fSDeng-Cheng Zhu 123d7d5b05fSDeng-Cheng Zhu tlb_probe(); 124d7d5b05fSDeng-Cheng Zhu tlb_probe_hazard(); 125d7d5b05fSDeng-Cheng Zhu idx = read_c0_index(); 126d7d5b05fSDeng-Cheng Zhu 127d7d5b05fSDeng-Cheng Zhu if (idx > current_cpu_data.tlbsize) { 128d7d5b05fSDeng-Cheng Zhu kvm_err("%s: Invalid Index: %d\n", __func__, idx); 129d7d5b05fSDeng-Cheng Zhu kvm_mips_dump_host_tlbs(); 130cfec0e75STapasweni Pathak local_irq_restore(flags); 131d7d5b05fSDeng-Cheng Zhu return -1; 132d7d5b05fSDeng-Cheng Zhu } 133d7d5b05fSDeng-Cheng Zhu 134d7d5b05fSDeng-Cheng Zhu write_c0_entrylo0(entrylo0); 135d7d5b05fSDeng-Cheng Zhu write_c0_entrylo1(entrylo1); 136d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 137d7d5b05fSDeng-Cheng Zhu 138d7d5b05fSDeng-Cheng Zhu if (idx < 0) 139d7d5b05fSDeng-Cheng Zhu tlb_write_random(); 140d7d5b05fSDeng-Cheng Zhu else 141d7d5b05fSDeng-Cheng Zhu tlb_write_indexed(); 142d7d5b05fSDeng-Cheng Zhu tlbw_use_hazard(); 143d7d5b05fSDeng-Cheng Zhu 144d7d5b05fSDeng-Cheng Zhu kvm_debug("@ %#lx idx: %2d [entryhi(R): %#lx] entrylo0(R): 0x%08lx, entrylo1(R): 0x%08lx\n", 145d7d5b05fSDeng-Cheng Zhu vcpu->arch.pc, idx, read_c0_entryhi(), 146d7d5b05fSDeng-Cheng Zhu read_c0_entrylo0(), read_c0_entrylo1()); 147d7d5b05fSDeng-Cheng Zhu 148d7d5b05fSDeng-Cheng Zhu /* Flush D-cache */ 149d7d5b05fSDeng-Cheng Zhu if (flush_dcache_mask) { 150e6207bbeSJames Hogan if (entrylo0 & ENTRYLO_V) { 151d7d5b05fSDeng-Cheng Zhu ++vcpu->stat.flush_dcache_exits; 152d7d5b05fSDeng-Cheng Zhu flush_data_cache_page((entryhi & VPN2_MASK) & 153d7d5b05fSDeng-Cheng Zhu ~flush_dcache_mask); 154d7d5b05fSDeng-Cheng Zhu } 155e6207bbeSJames Hogan if (entrylo1 & ENTRYLO_V) { 156d7d5b05fSDeng-Cheng Zhu ++vcpu->stat.flush_dcache_exits; 157d7d5b05fSDeng-Cheng Zhu flush_data_cache_page(((entryhi & VPN2_MASK) & 158d7d5b05fSDeng-Cheng Zhu ~flush_dcache_mask) | 159d7d5b05fSDeng-Cheng Zhu (0x1 << PAGE_SHIFT)); 160d7d5b05fSDeng-Cheng Zhu } 161d7d5b05fSDeng-Cheng Zhu } 162d7d5b05fSDeng-Cheng Zhu 163d7d5b05fSDeng-Cheng Zhu /* Restore old ASID */ 164d7d5b05fSDeng-Cheng Zhu write_c0_entryhi(old_entryhi); 165d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 166d7d5b05fSDeng-Cheng Zhu local_irq_restore(flags); 167d7d5b05fSDeng-Cheng Zhu return 0; 168d7d5b05fSDeng-Cheng Zhu } 169403015b3SJames Hogan EXPORT_SYMBOL_GPL(kvm_mips_host_tlb_write); 170d7d5b05fSDeng-Cheng Zhu 171d7d5b05fSDeng-Cheng Zhu int kvm_mips_handle_commpage_tlb_fault(unsigned long badvaddr, 172d7d5b05fSDeng-Cheng Zhu struct kvm_vcpu *vcpu) 173d7d5b05fSDeng-Cheng Zhu { 17442aa12e7SJames Hogan kvm_pfn_t pfn; 175d7d5b05fSDeng-Cheng Zhu unsigned long flags, old_entryhi = 0, vaddr = 0; 17642aa12e7SJames Hogan unsigned long entrylo[2] = { 0, 0 }; 17742aa12e7SJames Hogan unsigned int pair_idx; 178d7d5b05fSDeng-Cheng Zhu 179cfacacedSJames Hogan pfn = PFN_DOWN(virt_to_phys(vcpu->arch.kseg0_commpage)); 18042aa12e7SJames Hogan pair_idx = (badvaddr >> PAGE_SHIFT) & 1; 18142aa12e7SJames Hogan entrylo[pair_idx] = mips3_paddr_to_tlbpfn(pfn << PAGE_SHIFT) | 1827414d2f6SJames Hogan ((_page_cachable_default >> _CACHE_SHIFT) << ENTRYLO_C_SHIFT) | 1837414d2f6SJames Hogan ENTRYLO_D | ENTRYLO_V; 184d7d5b05fSDeng-Cheng Zhu 185d7d5b05fSDeng-Cheng Zhu local_irq_save(flags); 186d7d5b05fSDeng-Cheng Zhu 187d7d5b05fSDeng-Cheng Zhu old_entryhi = read_c0_entryhi(); 188d7d5b05fSDeng-Cheng Zhu vaddr = badvaddr & (PAGE_MASK << 1); 189d7d5b05fSDeng-Cheng Zhu write_c0_entryhi(vaddr | kvm_mips_get_kernel_asid(vcpu)); 19042aa12e7SJames Hogan write_c0_entrylo0(entrylo[0]); 19142aa12e7SJames Hogan write_c0_entrylo1(entrylo[1]); 192d7d5b05fSDeng-Cheng Zhu write_c0_index(kvm_mips_get_commpage_asid(vcpu)); 193d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 194d7d5b05fSDeng-Cheng Zhu tlb_write_indexed(); 195d7d5b05fSDeng-Cheng Zhu tlbw_use_hazard(); 196d7d5b05fSDeng-Cheng Zhu 197d7d5b05fSDeng-Cheng Zhu kvm_debug("@ %#lx idx: %2d [entryhi(R): %#lx] entrylo0 (R): 0x%08lx, entrylo1(R): 0x%08lx\n", 198d7d5b05fSDeng-Cheng Zhu vcpu->arch.pc, read_c0_index(), read_c0_entryhi(), 199d7d5b05fSDeng-Cheng Zhu read_c0_entrylo0(), read_c0_entrylo1()); 200d7d5b05fSDeng-Cheng Zhu 201d7d5b05fSDeng-Cheng Zhu /* Restore old ASID */ 202d7d5b05fSDeng-Cheng Zhu write_c0_entryhi(old_entryhi); 203d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 204d7d5b05fSDeng-Cheng Zhu local_irq_restore(flags); 205d7d5b05fSDeng-Cheng Zhu 206d7d5b05fSDeng-Cheng Zhu return 0; 207d7d5b05fSDeng-Cheng Zhu } 208cb1b447fSJames Hogan EXPORT_SYMBOL_GPL(kvm_mips_handle_commpage_tlb_fault); 209d7d5b05fSDeng-Cheng Zhu 210d7d5b05fSDeng-Cheng Zhu int kvm_mips_guest_tlb_lookup(struct kvm_vcpu *vcpu, unsigned long entryhi) 211d7d5b05fSDeng-Cheng Zhu { 212d7d5b05fSDeng-Cheng Zhu int i; 213d7d5b05fSDeng-Cheng Zhu int index = -1; 214d7d5b05fSDeng-Cheng Zhu struct kvm_mips_tlb *tlb = vcpu->arch.guest_tlb; 215d7d5b05fSDeng-Cheng Zhu 216d7d5b05fSDeng-Cheng Zhu for (i = 0; i < KVM_MIPS_GUEST_TLB_SIZE; i++) { 217d7d5b05fSDeng-Cheng Zhu if (TLB_HI_VPN2_HIT(tlb[i], entryhi) && 218d7d5b05fSDeng-Cheng Zhu TLB_HI_ASID_HIT(tlb[i], entryhi)) { 219d7d5b05fSDeng-Cheng Zhu index = i; 220d7d5b05fSDeng-Cheng Zhu break; 221d7d5b05fSDeng-Cheng Zhu } 222d7d5b05fSDeng-Cheng Zhu } 223d7d5b05fSDeng-Cheng Zhu 224d7d5b05fSDeng-Cheng Zhu kvm_debug("%s: entryhi: %#lx, index: %d lo0: %#lx, lo1: %#lx\n", 2259fbfb06aSJames Hogan __func__, entryhi, index, tlb[i].tlb_lo[0], tlb[i].tlb_lo[1]); 226d7d5b05fSDeng-Cheng Zhu 227d7d5b05fSDeng-Cheng Zhu return index; 228d7d5b05fSDeng-Cheng Zhu } 229cb1b447fSJames Hogan EXPORT_SYMBOL_GPL(kvm_mips_guest_tlb_lookup); 230d7d5b05fSDeng-Cheng Zhu 231d7d5b05fSDeng-Cheng Zhu int kvm_mips_host_tlb_lookup(struct kvm_vcpu *vcpu, unsigned long vaddr) 232d7d5b05fSDeng-Cheng Zhu { 233d7d5b05fSDeng-Cheng Zhu unsigned long old_entryhi, flags; 234d7d5b05fSDeng-Cheng Zhu int idx; 235d7d5b05fSDeng-Cheng Zhu 236d7d5b05fSDeng-Cheng Zhu local_irq_save(flags); 237d7d5b05fSDeng-Cheng Zhu 238d7d5b05fSDeng-Cheng Zhu old_entryhi = read_c0_entryhi(); 239d7d5b05fSDeng-Cheng Zhu 240d7d5b05fSDeng-Cheng Zhu if (KVM_GUEST_KERNEL_MODE(vcpu)) 241d7d5b05fSDeng-Cheng Zhu write_c0_entryhi((vaddr & VPN2_MASK) | 242d7d5b05fSDeng-Cheng Zhu kvm_mips_get_kernel_asid(vcpu)); 243d7d5b05fSDeng-Cheng Zhu else { 244d7d5b05fSDeng-Cheng Zhu write_c0_entryhi((vaddr & VPN2_MASK) | 245d7d5b05fSDeng-Cheng Zhu kvm_mips_get_user_asid(vcpu)); 246d7d5b05fSDeng-Cheng Zhu } 247d7d5b05fSDeng-Cheng Zhu 248d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 249d7d5b05fSDeng-Cheng Zhu 250d7d5b05fSDeng-Cheng Zhu tlb_probe(); 251d7d5b05fSDeng-Cheng Zhu tlb_probe_hazard(); 252d7d5b05fSDeng-Cheng Zhu idx = read_c0_index(); 253d7d5b05fSDeng-Cheng Zhu 254d7d5b05fSDeng-Cheng Zhu /* Restore old ASID */ 255d7d5b05fSDeng-Cheng Zhu write_c0_entryhi(old_entryhi); 256d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 257d7d5b05fSDeng-Cheng Zhu 258d7d5b05fSDeng-Cheng Zhu local_irq_restore(flags); 259d7d5b05fSDeng-Cheng Zhu 260d7d5b05fSDeng-Cheng Zhu kvm_debug("Host TLB lookup, %#lx, idx: %2d\n", vaddr, idx); 261d7d5b05fSDeng-Cheng Zhu 262d7d5b05fSDeng-Cheng Zhu return idx; 263d7d5b05fSDeng-Cheng Zhu } 264cb1b447fSJames Hogan EXPORT_SYMBOL_GPL(kvm_mips_host_tlb_lookup); 265d7d5b05fSDeng-Cheng Zhu 26657e3869cSJames Hogan static int _kvm_mips_host_tlb_inv(unsigned long entryhi) 267d7d5b05fSDeng-Cheng Zhu { 268d7d5b05fSDeng-Cheng Zhu int idx; 269d7d5b05fSDeng-Cheng Zhu 27057e3869cSJames Hogan write_c0_entryhi(entryhi); 271d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 272d7d5b05fSDeng-Cheng Zhu 273d7d5b05fSDeng-Cheng Zhu tlb_probe(); 274d7d5b05fSDeng-Cheng Zhu tlb_probe_hazard(); 275d7d5b05fSDeng-Cheng Zhu idx = read_c0_index(); 276d7d5b05fSDeng-Cheng Zhu 277d7d5b05fSDeng-Cheng Zhu if (idx >= current_cpu_data.tlbsize) 278d7d5b05fSDeng-Cheng Zhu BUG(); 279d7d5b05fSDeng-Cheng Zhu 280f3a8603fSJames Hogan if (idx >= 0) { 281d7d5b05fSDeng-Cheng Zhu write_c0_entryhi(UNIQUE_ENTRYHI(idx)); 282d7d5b05fSDeng-Cheng Zhu write_c0_entrylo0(0); 283d7d5b05fSDeng-Cheng Zhu write_c0_entrylo1(0); 284d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 285d7d5b05fSDeng-Cheng Zhu 286d7d5b05fSDeng-Cheng Zhu tlb_write_indexed(); 287138f7ad9SJames Hogan tlbw_use_hazard(); 288d7d5b05fSDeng-Cheng Zhu } 289d7d5b05fSDeng-Cheng Zhu 29057e3869cSJames Hogan return idx; 29157e3869cSJames Hogan } 29257e3869cSJames Hogan 29357e3869cSJames Hogan int kvm_mips_host_tlb_inv(struct kvm_vcpu *vcpu, unsigned long va, 29457e3869cSJames Hogan bool user, bool kernel) 29557e3869cSJames Hogan { 29657e3869cSJames Hogan int idx_user, idx_kernel; 29757e3869cSJames Hogan unsigned long flags, old_entryhi; 29857e3869cSJames Hogan 29957e3869cSJames Hogan local_irq_save(flags); 30057e3869cSJames Hogan 30157e3869cSJames Hogan old_entryhi = read_c0_entryhi(); 30257e3869cSJames Hogan 30357e3869cSJames Hogan if (user) 30457e3869cSJames Hogan idx_user = _kvm_mips_host_tlb_inv((va & VPN2_MASK) | 30557e3869cSJames Hogan kvm_mips_get_user_asid(vcpu)); 30657e3869cSJames Hogan if (kernel) 30757e3869cSJames Hogan idx_kernel = _kvm_mips_host_tlb_inv((va & VPN2_MASK) | 30857e3869cSJames Hogan kvm_mips_get_kernel_asid(vcpu)); 30957e3869cSJames Hogan 310d7d5b05fSDeng-Cheng Zhu write_c0_entryhi(old_entryhi); 311d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 312d7d5b05fSDeng-Cheng Zhu 313d7d5b05fSDeng-Cheng Zhu local_irq_restore(flags); 314d7d5b05fSDeng-Cheng Zhu 31557e3869cSJames Hogan if (user && idx_user >= 0) 31657e3869cSJames Hogan kvm_debug("%s: Invalidated guest user entryhi %#lx @ idx %d\n", 31757e3869cSJames Hogan __func__, (va & VPN2_MASK) | 31857e3869cSJames Hogan kvm_mips_get_user_asid(vcpu), idx_user); 31957e3869cSJames Hogan if (kernel && idx_kernel >= 0) 32057e3869cSJames Hogan kvm_debug("%s: Invalidated guest kernel entryhi %#lx @ idx %d\n", 32157e3869cSJames Hogan __func__, (va & VPN2_MASK) | 32257e3869cSJames Hogan kvm_mips_get_kernel_asid(vcpu), idx_kernel); 323d7d5b05fSDeng-Cheng Zhu 324d7d5b05fSDeng-Cheng Zhu return 0; 325d7d5b05fSDeng-Cheng Zhu } 326cb1b447fSJames Hogan EXPORT_SYMBOL_GPL(kvm_mips_host_tlb_inv); 327d7d5b05fSDeng-Cheng Zhu 328d7d5b05fSDeng-Cheng Zhu void kvm_mips_flush_host_tlb(int skip_kseg0) 329d7d5b05fSDeng-Cheng Zhu { 330d7d5b05fSDeng-Cheng Zhu unsigned long flags; 331d7d5b05fSDeng-Cheng Zhu unsigned long old_entryhi, entryhi; 332d7d5b05fSDeng-Cheng Zhu unsigned long old_pagemask; 333d7d5b05fSDeng-Cheng Zhu int entry = 0; 334d7d5b05fSDeng-Cheng Zhu int maxentry = current_cpu_data.tlbsize; 335d7d5b05fSDeng-Cheng Zhu 336d7d5b05fSDeng-Cheng Zhu local_irq_save(flags); 337d7d5b05fSDeng-Cheng Zhu 338d7d5b05fSDeng-Cheng Zhu old_entryhi = read_c0_entryhi(); 339d7d5b05fSDeng-Cheng Zhu old_pagemask = read_c0_pagemask(); 340d7d5b05fSDeng-Cheng Zhu 341d7d5b05fSDeng-Cheng Zhu /* Blast 'em all away. */ 342d7d5b05fSDeng-Cheng Zhu for (entry = 0; entry < maxentry; entry++) { 343d7d5b05fSDeng-Cheng Zhu write_c0_index(entry); 344d7d5b05fSDeng-Cheng Zhu 345d7d5b05fSDeng-Cheng Zhu if (skip_kseg0) { 346138f7ad9SJames Hogan mtc0_tlbr_hazard(); 347d7d5b05fSDeng-Cheng Zhu tlb_read(); 348138f7ad9SJames Hogan tlb_read_hazard(); 349d7d5b05fSDeng-Cheng Zhu 350d7d5b05fSDeng-Cheng Zhu entryhi = read_c0_entryhi(); 351d7d5b05fSDeng-Cheng Zhu 352d7d5b05fSDeng-Cheng Zhu /* Don't blow away guest kernel entries */ 353d7d5b05fSDeng-Cheng Zhu if (KVM_GUEST_KSEGX(entryhi) == KVM_GUEST_KSEG0) 354d7d5b05fSDeng-Cheng Zhu continue; 355a700434dSJames Hogan 356a700434dSJames Hogan write_c0_pagemask(old_pagemask); 357d7d5b05fSDeng-Cheng Zhu } 358d7d5b05fSDeng-Cheng Zhu 359d7d5b05fSDeng-Cheng Zhu /* Make sure all entries differ. */ 360d7d5b05fSDeng-Cheng Zhu write_c0_entryhi(UNIQUE_ENTRYHI(entry)); 361d7d5b05fSDeng-Cheng Zhu write_c0_entrylo0(0); 362d7d5b05fSDeng-Cheng Zhu write_c0_entrylo1(0); 363d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 364d7d5b05fSDeng-Cheng Zhu 365d7d5b05fSDeng-Cheng Zhu tlb_write_indexed(); 366d7d5b05fSDeng-Cheng Zhu tlbw_use_hazard(); 367138f7ad9SJames Hogan } 368d7d5b05fSDeng-Cheng Zhu 369d7d5b05fSDeng-Cheng Zhu write_c0_entryhi(old_entryhi); 370d7d5b05fSDeng-Cheng Zhu write_c0_pagemask(old_pagemask); 371d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 372d7d5b05fSDeng-Cheng Zhu 373d7d5b05fSDeng-Cheng Zhu local_irq_restore(flags); 374d7d5b05fSDeng-Cheng Zhu } 375cb1b447fSJames Hogan EXPORT_SYMBOL_GPL(kvm_mips_flush_host_tlb); 376d7d5b05fSDeng-Cheng Zhu 377d7d5b05fSDeng-Cheng Zhu void kvm_local_flush_tlb_all(void) 378d7d5b05fSDeng-Cheng Zhu { 379d7d5b05fSDeng-Cheng Zhu unsigned long flags; 380d7d5b05fSDeng-Cheng Zhu unsigned long old_ctx; 381d7d5b05fSDeng-Cheng Zhu int entry = 0; 382d7d5b05fSDeng-Cheng Zhu 383d7d5b05fSDeng-Cheng Zhu local_irq_save(flags); 384d7d5b05fSDeng-Cheng Zhu /* Save old context and create impossible VPN2 value */ 385d7d5b05fSDeng-Cheng Zhu old_ctx = read_c0_entryhi(); 386d7d5b05fSDeng-Cheng Zhu write_c0_entrylo0(0); 387d7d5b05fSDeng-Cheng Zhu write_c0_entrylo1(0); 388d7d5b05fSDeng-Cheng Zhu 389d7d5b05fSDeng-Cheng Zhu /* Blast 'em all away. */ 390d7d5b05fSDeng-Cheng Zhu while (entry < current_cpu_data.tlbsize) { 391d7d5b05fSDeng-Cheng Zhu /* Make sure all entries differ. */ 392d7d5b05fSDeng-Cheng Zhu write_c0_entryhi(UNIQUE_ENTRYHI(entry)); 393d7d5b05fSDeng-Cheng Zhu write_c0_index(entry); 394d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 395d7d5b05fSDeng-Cheng Zhu tlb_write_indexed(); 396138f7ad9SJames Hogan tlbw_use_hazard(); 397d7d5b05fSDeng-Cheng Zhu entry++; 398d7d5b05fSDeng-Cheng Zhu } 399d7d5b05fSDeng-Cheng Zhu write_c0_entryhi(old_ctx); 400d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 401d7d5b05fSDeng-Cheng Zhu 402d7d5b05fSDeng-Cheng Zhu local_irq_restore(flags); 403d7d5b05fSDeng-Cheng Zhu } 404cb1b447fSJames Hogan EXPORT_SYMBOL_GPL(kvm_local_flush_tlb_all); 405a7ebb2e4SJames Hogan 406a7ebb2e4SJames Hogan /** 407a7ebb2e4SJames Hogan * kvm_mips_suspend_mm() - Suspend the active mm. 408a7ebb2e4SJames Hogan * @cpu The CPU we're running on. 409a7ebb2e4SJames Hogan * 410a7ebb2e4SJames Hogan * Suspend the active_mm, ready for a switch to a KVM guest virtual address 411a7ebb2e4SJames Hogan * space. This is left active for the duration of guest context, including time 412a7ebb2e4SJames Hogan * with interrupts enabled, so we need to be careful not to confuse e.g. cache 413a7ebb2e4SJames Hogan * management IPIs. 414a7ebb2e4SJames Hogan * 415a7ebb2e4SJames Hogan * kvm_mips_resume_mm() should be called before context switching to a different 416a7ebb2e4SJames Hogan * process so we don't need to worry about reference counting. 417a7ebb2e4SJames Hogan * 418a7ebb2e4SJames Hogan * This needs to be in static kernel code to avoid exporting init_mm. 419a7ebb2e4SJames Hogan */ 420a7ebb2e4SJames Hogan void kvm_mips_suspend_mm(int cpu) 421a7ebb2e4SJames Hogan { 422a7ebb2e4SJames Hogan cpumask_clear_cpu(cpu, mm_cpumask(current->active_mm)); 423a7ebb2e4SJames Hogan current->active_mm = &init_mm; 424a7ebb2e4SJames Hogan } 425a7ebb2e4SJames Hogan EXPORT_SYMBOL_GPL(kvm_mips_suspend_mm); 426a7ebb2e4SJames Hogan 427a7ebb2e4SJames Hogan /** 428a7ebb2e4SJames Hogan * kvm_mips_resume_mm() - Resume the current process mm. 429a7ebb2e4SJames Hogan * @cpu The CPU we're running on. 430a7ebb2e4SJames Hogan * 431a7ebb2e4SJames Hogan * Resume the mm of the current process, after a switch back from a KVM guest 432a7ebb2e4SJames Hogan * virtual address space (see kvm_mips_suspend_mm()). 433a7ebb2e4SJames Hogan */ 434a7ebb2e4SJames Hogan void kvm_mips_resume_mm(int cpu) 435a7ebb2e4SJames Hogan { 436a7ebb2e4SJames Hogan cpumask_set_cpu(cpu, mm_cpumask(current->mm)); 437a7ebb2e4SJames Hogan current->active_mm = current->mm; 438a7ebb2e4SJames Hogan } 439a7ebb2e4SJames Hogan EXPORT_SYMBOL_GPL(kvm_mips_resume_mm); 440