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> 238a5097eeSHuacai Chen #include <asm/mipsregs.h> 24d7d5b05fSDeng-Cheng Zhu #include <asm/mmu_context.h> 25d7d5b05fSDeng-Cheng Zhu #include <asm/pgtable.h> 26d7d5b05fSDeng-Cheng Zhu #include <asm/cacheflush.h> 27d7d5b05fSDeng-Cheng Zhu #include <asm/tlb.h> 28e922a4cbSJames Hogan #include <asm/tlbdebug.h> 29d7d5b05fSDeng-Cheng Zhu 30d7d5b05fSDeng-Cheng Zhu #undef CONFIG_MIPS_MT 31d7d5b05fSDeng-Cheng Zhu #include <asm/r4kcache.h> 32d7d5b05fSDeng-Cheng Zhu #define CONFIG_MIPS_MT 33d7d5b05fSDeng-Cheng Zhu 34d7d5b05fSDeng-Cheng Zhu #define KVM_GUEST_PC_TLB 0 35d7d5b05fSDeng-Cheng Zhu #define KVM_GUEST_SP_TLB 1 36d7d5b05fSDeng-Cheng Zhu 37372582a6SJames Hogan #ifdef CONFIG_KVM_MIPS_VZ 38c992a4f6SJames Hogan unsigned long GUESTID_MASK; 39c992a4f6SJames Hogan EXPORT_SYMBOL_GPL(GUESTID_MASK); 40c992a4f6SJames Hogan unsigned long GUESTID_FIRST_VERSION; 41c992a4f6SJames Hogan EXPORT_SYMBOL_GPL(GUESTID_FIRST_VERSION); 42c992a4f6SJames Hogan unsigned long GUESTID_VERSION_MASK; 43c992a4f6SJames Hogan EXPORT_SYMBOL_GPL(GUESTID_VERSION_MASK); 44c992a4f6SJames Hogan 45372582a6SJames Hogan static u32 kvm_mips_get_root_asid(struct kvm_vcpu *vcpu) 46372582a6SJames Hogan { 47372582a6SJames Hogan struct mm_struct *gpa_mm = &vcpu->kvm->arch.gpa_mm; 48372582a6SJames Hogan 49372582a6SJames Hogan if (cpu_has_guestid) 50372582a6SJames Hogan return 0; 51372582a6SJames Hogan else 52372582a6SJames Hogan return cpu_asid(smp_processor_id(), gpa_mm); 53372582a6SJames Hogan } 54372582a6SJames Hogan #endif 55372582a6SJames Hogan 56403015b3SJames Hogan static u32 kvm_mips_get_kernel_asid(struct kvm_vcpu *vcpu) 57d7d5b05fSDeng-Cheng Zhu { 58c550d539SJames Hogan struct mm_struct *kern_mm = &vcpu->arch.guest_kernel_mm; 594edf00a4SPaul Burton int cpu = smp_processor_id(); 604edf00a4SPaul Burton 61c550d539SJames Hogan return cpu_asid(cpu, kern_mm); 62d7d5b05fSDeng-Cheng Zhu } 63d7d5b05fSDeng-Cheng Zhu 64403015b3SJames Hogan static u32 kvm_mips_get_user_asid(struct kvm_vcpu *vcpu) 65d7d5b05fSDeng-Cheng Zhu { 66c550d539SJames Hogan struct mm_struct *user_mm = &vcpu->arch.guest_user_mm; 674edf00a4SPaul Burton int cpu = smp_processor_id(); 684edf00a4SPaul Burton 69c550d539SJames Hogan return cpu_asid(cpu, user_mm); 70d7d5b05fSDeng-Cheng Zhu } 71d7d5b05fSDeng-Cheng Zhu 72d7d5b05fSDeng-Cheng Zhu /* Structure defining an tlb entry data set. */ 73d7d5b05fSDeng-Cheng Zhu 74d7d5b05fSDeng-Cheng Zhu void kvm_mips_dump_host_tlbs(void) 75d7d5b05fSDeng-Cheng Zhu { 76d7d5b05fSDeng-Cheng Zhu unsigned long flags; 77d7d5b05fSDeng-Cheng Zhu 78d7d5b05fSDeng-Cheng Zhu local_irq_save(flags); 79d7d5b05fSDeng-Cheng Zhu 80d7d5b05fSDeng-Cheng Zhu kvm_info("HOST TLBs:\n"); 81e922a4cbSJames Hogan dump_tlb_regs(); 82e922a4cbSJames Hogan pr_info("\n"); 83e922a4cbSJames Hogan dump_tlb_all(); 84d7d5b05fSDeng-Cheng Zhu 85d7d5b05fSDeng-Cheng Zhu local_irq_restore(flags); 86d7d5b05fSDeng-Cheng Zhu } 87cb1b447fSJames Hogan EXPORT_SYMBOL_GPL(kvm_mips_dump_host_tlbs); 88d7d5b05fSDeng-Cheng Zhu 89d7d5b05fSDeng-Cheng Zhu void kvm_mips_dump_guest_tlbs(struct kvm_vcpu *vcpu) 90d7d5b05fSDeng-Cheng Zhu { 91d7d5b05fSDeng-Cheng Zhu struct mips_coproc *cop0 = vcpu->arch.cop0; 92d7d5b05fSDeng-Cheng Zhu struct kvm_mips_tlb tlb; 93d7d5b05fSDeng-Cheng Zhu int i; 94d7d5b05fSDeng-Cheng Zhu 95d7d5b05fSDeng-Cheng Zhu kvm_info("Guest TLBs:\n"); 96d7d5b05fSDeng-Cheng Zhu kvm_info("Guest EntryHi: %#lx\n", kvm_read_c0_guest_entryhi(cop0)); 97d7d5b05fSDeng-Cheng Zhu 98d7d5b05fSDeng-Cheng Zhu for (i = 0; i < KVM_MIPS_GUEST_TLB_SIZE; i++) { 99d7d5b05fSDeng-Cheng Zhu tlb = vcpu->arch.guest_tlb[i]; 100d7d5b05fSDeng-Cheng Zhu kvm_info("TLB%c%3d Hi 0x%08lx ", 101e6207bbeSJames Hogan (tlb.tlb_lo[0] | tlb.tlb_lo[1]) & ENTRYLO_V 1029fbfb06aSJames Hogan ? ' ' : '*', 103d7d5b05fSDeng-Cheng Zhu i, tlb.tlb_hi); 1048cffd197SJames Hogan kvm_info("Lo0=0x%09llx %c%c attr %lx ", 1059fbfb06aSJames Hogan (u64) mips3_tlbpfn_to_paddr(tlb.tlb_lo[0]), 106e6207bbeSJames Hogan (tlb.tlb_lo[0] & ENTRYLO_D) ? 'D' : ' ', 107e6207bbeSJames Hogan (tlb.tlb_lo[0] & ENTRYLO_G) ? 'G' : ' ', 108e6207bbeSJames Hogan (tlb.tlb_lo[0] & ENTRYLO_C) >> ENTRYLO_C_SHIFT); 1098cffd197SJames Hogan kvm_info("Lo1=0x%09llx %c%c attr %lx sz=%lx\n", 1109fbfb06aSJames Hogan (u64) mips3_tlbpfn_to_paddr(tlb.tlb_lo[1]), 111e6207bbeSJames Hogan (tlb.tlb_lo[1] & ENTRYLO_D) ? 'D' : ' ', 112e6207bbeSJames Hogan (tlb.tlb_lo[1] & ENTRYLO_G) ? 'G' : ' ', 113e6207bbeSJames Hogan (tlb.tlb_lo[1] & ENTRYLO_C) >> ENTRYLO_C_SHIFT, 114e6207bbeSJames Hogan tlb.tlb_mask); 115d7d5b05fSDeng-Cheng Zhu } 116d7d5b05fSDeng-Cheng Zhu } 117cb1b447fSJames Hogan EXPORT_SYMBOL_GPL(kvm_mips_dump_guest_tlbs); 118d7d5b05fSDeng-Cheng Zhu 119d7d5b05fSDeng-Cheng Zhu int kvm_mips_guest_tlb_lookup(struct kvm_vcpu *vcpu, unsigned long entryhi) 120d7d5b05fSDeng-Cheng Zhu { 121d7d5b05fSDeng-Cheng Zhu int i; 122d7d5b05fSDeng-Cheng Zhu int index = -1; 123d7d5b05fSDeng-Cheng Zhu struct kvm_mips_tlb *tlb = vcpu->arch.guest_tlb; 124d7d5b05fSDeng-Cheng Zhu 125d7d5b05fSDeng-Cheng Zhu for (i = 0; i < KVM_MIPS_GUEST_TLB_SIZE; i++) { 126d7d5b05fSDeng-Cheng Zhu if (TLB_HI_VPN2_HIT(tlb[i], entryhi) && 127d7d5b05fSDeng-Cheng Zhu TLB_HI_ASID_HIT(tlb[i], entryhi)) { 128d7d5b05fSDeng-Cheng Zhu index = i; 129d7d5b05fSDeng-Cheng Zhu break; 130d7d5b05fSDeng-Cheng Zhu } 131d7d5b05fSDeng-Cheng Zhu } 132d7d5b05fSDeng-Cheng Zhu 133d7d5b05fSDeng-Cheng Zhu kvm_debug("%s: entryhi: %#lx, index: %d lo0: %#lx, lo1: %#lx\n", 1349fbfb06aSJames Hogan __func__, entryhi, index, tlb[i].tlb_lo[0], tlb[i].tlb_lo[1]); 135d7d5b05fSDeng-Cheng Zhu 136d7d5b05fSDeng-Cheng Zhu return index; 137d7d5b05fSDeng-Cheng Zhu } 138cb1b447fSJames Hogan EXPORT_SYMBOL_GPL(kvm_mips_guest_tlb_lookup); 139d7d5b05fSDeng-Cheng Zhu 14057e3869cSJames Hogan static int _kvm_mips_host_tlb_inv(unsigned long entryhi) 141d7d5b05fSDeng-Cheng Zhu { 142d7d5b05fSDeng-Cheng Zhu int idx; 143d7d5b05fSDeng-Cheng Zhu 14457e3869cSJames Hogan write_c0_entryhi(entryhi); 145d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 146d7d5b05fSDeng-Cheng Zhu 147d7d5b05fSDeng-Cheng Zhu tlb_probe(); 148d7d5b05fSDeng-Cheng Zhu tlb_probe_hazard(); 149d7d5b05fSDeng-Cheng Zhu idx = read_c0_index(); 150d7d5b05fSDeng-Cheng Zhu 151d7d5b05fSDeng-Cheng Zhu if (idx >= current_cpu_data.tlbsize) 152d7d5b05fSDeng-Cheng Zhu BUG(); 153d7d5b05fSDeng-Cheng Zhu 154f3a8603fSJames Hogan if (idx >= 0) { 155d7d5b05fSDeng-Cheng Zhu write_c0_entryhi(UNIQUE_ENTRYHI(idx)); 156d7d5b05fSDeng-Cheng Zhu write_c0_entrylo0(0); 157d7d5b05fSDeng-Cheng Zhu write_c0_entrylo1(0); 158d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 159d7d5b05fSDeng-Cheng Zhu 160d7d5b05fSDeng-Cheng Zhu tlb_write_indexed(); 161138f7ad9SJames Hogan tlbw_use_hazard(); 162d7d5b05fSDeng-Cheng Zhu } 163d7d5b05fSDeng-Cheng Zhu 16457e3869cSJames Hogan return idx; 16557e3869cSJames Hogan } 16657e3869cSJames Hogan 16757e3869cSJames Hogan int kvm_mips_host_tlb_inv(struct kvm_vcpu *vcpu, unsigned long va, 16857e3869cSJames Hogan bool user, bool kernel) 16957e3869cSJames Hogan { 170e27a9ecaSJames Cowgill /* 171e27a9ecaSJames Cowgill * Initialize idx_user and idx_kernel to workaround bogus 172e27a9ecaSJames Cowgill * maybe-initialized warning when using GCC 6. 173e27a9ecaSJames Cowgill */ 174e27a9ecaSJames Cowgill int idx_user = 0, idx_kernel = 0; 17557e3869cSJames Hogan unsigned long flags, old_entryhi; 17657e3869cSJames Hogan 17757e3869cSJames Hogan local_irq_save(flags); 17857e3869cSJames Hogan 17957e3869cSJames Hogan old_entryhi = read_c0_entryhi(); 18057e3869cSJames Hogan 18157e3869cSJames Hogan if (user) 18257e3869cSJames Hogan idx_user = _kvm_mips_host_tlb_inv((va & VPN2_MASK) | 18357e3869cSJames Hogan kvm_mips_get_user_asid(vcpu)); 18457e3869cSJames Hogan if (kernel) 18557e3869cSJames Hogan idx_kernel = _kvm_mips_host_tlb_inv((va & VPN2_MASK) | 18657e3869cSJames Hogan kvm_mips_get_kernel_asid(vcpu)); 18757e3869cSJames Hogan 188d7d5b05fSDeng-Cheng Zhu write_c0_entryhi(old_entryhi); 189d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 190d7d5b05fSDeng-Cheng Zhu 191d7d5b05fSDeng-Cheng Zhu local_irq_restore(flags); 192d7d5b05fSDeng-Cheng Zhu 1931c506c9cSJames Hogan /* 1941c506c9cSJames Hogan * We don't want to get reserved instruction exceptions for missing tlb 1951c506c9cSJames Hogan * entries. 1961c506c9cSJames Hogan */ 1971c506c9cSJames Hogan if (cpu_has_vtag_icache) 1981c506c9cSJames Hogan flush_icache_all(); 1991c506c9cSJames Hogan 20057e3869cSJames Hogan if (user && idx_user >= 0) 20157e3869cSJames Hogan kvm_debug("%s: Invalidated guest user entryhi %#lx @ idx %d\n", 20257e3869cSJames Hogan __func__, (va & VPN2_MASK) | 20357e3869cSJames Hogan kvm_mips_get_user_asid(vcpu), idx_user); 20457e3869cSJames Hogan if (kernel && idx_kernel >= 0) 20557e3869cSJames Hogan kvm_debug("%s: Invalidated guest kernel entryhi %#lx @ idx %d\n", 20657e3869cSJames Hogan __func__, (va & VPN2_MASK) | 20757e3869cSJames Hogan kvm_mips_get_kernel_asid(vcpu), idx_kernel); 208d7d5b05fSDeng-Cheng Zhu 209d7d5b05fSDeng-Cheng Zhu return 0; 210d7d5b05fSDeng-Cheng Zhu } 211cb1b447fSJames Hogan EXPORT_SYMBOL_GPL(kvm_mips_host_tlb_inv); 212d7d5b05fSDeng-Cheng Zhu 213372582a6SJames Hogan #ifdef CONFIG_KVM_MIPS_VZ 214372582a6SJames Hogan 215372582a6SJames Hogan /* GuestID management */ 216372582a6SJames Hogan 217372582a6SJames Hogan /** 218372582a6SJames Hogan * clear_root_gid() - Set GuestCtl1.RID for normal root operation. 219372582a6SJames Hogan */ 220372582a6SJames Hogan static inline void clear_root_gid(void) 221372582a6SJames Hogan { 222372582a6SJames Hogan if (cpu_has_guestid) { 223372582a6SJames Hogan clear_c0_guestctl1(MIPS_GCTL1_RID); 224372582a6SJames Hogan mtc0_tlbw_hazard(); 225372582a6SJames Hogan } 226372582a6SJames Hogan } 227372582a6SJames Hogan 228372582a6SJames Hogan /** 229372582a6SJames Hogan * set_root_gid_to_guest_gid() - Set GuestCtl1.RID to match GuestCtl1.ID. 230372582a6SJames Hogan * 231372582a6SJames Hogan * Sets the root GuestID to match the current guest GuestID, for TLB operation 232372582a6SJames Hogan * on the GPA->RPA mappings in the root TLB. 233372582a6SJames Hogan * 234372582a6SJames Hogan * The caller must be sure to disable HTW while the root GID is set, and 235372582a6SJames Hogan * possibly longer if TLB registers are modified. 236372582a6SJames Hogan */ 237372582a6SJames Hogan static inline void set_root_gid_to_guest_gid(void) 238372582a6SJames Hogan { 239372582a6SJames Hogan unsigned int guestctl1; 240372582a6SJames Hogan 241372582a6SJames Hogan if (cpu_has_guestid) { 242372582a6SJames Hogan back_to_back_c0_hazard(); 243372582a6SJames Hogan guestctl1 = read_c0_guestctl1(); 244372582a6SJames Hogan guestctl1 = (guestctl1 & ~MIPS_GCTL1_RID) | 245372582a6SJames Hogan ((guestctl1 & MIPS_GCTL1_ID) >> MIPS_GCTL1_ID_SHIFT) 246372582a6SJames Hogan << MIPS_GCTL1_RID_SHIFT; 247372582a6SJames Hogan write_c0_guestctl1(guestctl1); 248372582a6SJames Hogan mtc0_tlbw_hazard(); 249372582a6SJames Hogan } 250372582a6SJames Hogan } 251372582a6SJames Hogan 252372582a6SJames Hogan int kvm_vz_host_tlb_inv(struct kvm_vcpu *vcpu, unsigned long va) 253372582a6SJames Hogan { 254372582a6SJames Hogan int idx; 255372582a6SJames Hogan unsigned long flags, old_entryhi; 256372582a6SJames Hogan 257372582a6SJames Hogan local_irq_save(flags); 258372582a6SJames Hogan htw_stop(); 259372582a6SJames Hogan 260372582a6SJames Hogan /* Set root GuestID for root probe and write of guest TLB entry */ 261372582a6SJames Hogan set_root_gid_to_guest_gid(); 262372582a6SJames Hogan 263372582a6SJames Hogan old_entryhi = read_c0_entryhi(); 264372582a6SJames Hogan 265372582a6SJames Hogan idx = _kvm_mips_host_tlb_inv((va & VPN2_MASK) | 266372582a6SJames Hogan kvm_mips_get_root_asid(vcpu)); 267372582a6SJames Hogan 268372582a6SJames Hogan write_c0_entryhi(old_entryhi); 269372582a6SJames Hogan clear_root_gid(); 270372582a6SJames Hogan mtc0_tlbw_hazard(); 271372582a6SJames Hogan 272372582a6SJames Hogan htw_start(); 273372582a6SJames Hogan local_irq_restore(flags); 274372582a6SJames Hogan 2751c506c9cSJames Hogan /* 2761c506c9cSJames Hogan * We don't want to get reserved instruction exceptions for missing tlb 2771c506c9cSJames Hogan * entries. 2781c506c9cSJames Hogan */ 2791c506c9cSJames Hogan if (cpu_has_vtag_icache) 2801c506c9cSJames Hogan flush_icache_all(); 2811c506c9cSJames Hogan 282372582a6SJames Hogan if (idx > 0) 283372582a6SJames Hogan kvm_debug("%s: Invalidated root entryhi %#lx @ idx %d\n", 284372582a6SJames Hogan __func__, (va & VPN2_MASK) | 285372582a6SJames Hogan kvm_mips_get_root_asid(vcpu), idx); 286372582a6SJames Hogan 287372582a6SJames Hogan return 0; 288372582a6SJames Hogan } 289372582a6SJames Hogan EXPORT_SYMBOL_GPL(kvm_vz_host_tlb_inv); 290372582a6SJames Hogan 291372582a6SJames Hogan /** 292372582a6SJames Hogan * kvm_vz_guest_tlb_lookup() - Lookup a guest VZ TLB mapping. 293372582a6SJames Hogan * @vcpu: KVM VCPU pointer. 294372582a6SJames Hogan * @gpa: Guest virtual address in a TLB mapped guest segment. 295372582a6SJames Hogan * @gpa: Ponter to output guest physical address it maps to. 296372582a6SJames Hogan * 297372582a6SJames Hogan * Converts a guest virtual address in a guest TLB mapped segment to a guest 298372582a6SJames Hogan * physical address, by probing the guest TLB. 299372582a6SJames Hogan * 300372582a6SJames Hogan * Returns: 0 if guest TLB mapping exists for @gva. *@gpa will have been 301372582a6SJames Hogan * written. 302372582a6SJames Hogan * -EFAULT if no guest TLB mapping exists for @gva. *@gpa may not 303372582a6SJames Hogan * have been written. 304372582a6SJames Hogan */ 305372582a6SJames Hogan int kvm_vz_guest_tlb_lookup(struct kvm_vcpu *vcpu, unsigned long gva, 306372582a6SJames Hogan unsigned long *gpa) 307372582a6SJames Hogan { 308372582a6SJames Hogan unsigned long o_entryhi, o_entrylo[2], o_pagemask; 309372582a6SJames Hogan unsigned int o_index; 310372582a6SJames Hogan unsigned long entrylo[2], pagemask, pagemaskbit, pa; 311372582a6SJames Hogan unsigned long flags; 312372582a6SJames Hogan int index; 313372582a6SJames Hogan 314372582a6SJames Hogan /* Probe the guest TLB for a mapping */ 315372582a6SJames Hogan local_irq_save(flags); 316372582a6SJames Hogan /* Set root GuestID for root probe of guest TLB entry */ 317372582a6SJames Hogan htw_stop(); 318372582a6SJames Hogan set_root_gid_to_guest_gid(); 319372582a6SJames Hogan 320372582a6SJames Hogan o_entryhi = read_gc0_entryhi(); 321372582a6SJames Hogan o_index = read_gc0_index(); 322372582a6SJames Hogan 323372582a6SJames Hogan write_gc0_entryhi((o_entryhi & 0x3ff) | (gva & ~0xfffl)); 324372582a6SJames Hogan mtc0_tlbw_hazard(); 325372582a6SJames Hogan guest_tlb_probe(); 326372582a6SJames Hogan tlb_probe_hazard(); 327372582a6SJames Hogan 328372582a6SJames Hogan index = read_gc0_index(); 329372582a6SJames Hogan if (index < 0) { 330372582a6SJames Hogan /* No match, fail */ 331372582a6SJames Hogan write_gc0_entryhi(o_entryhi); 332372582a6SJames Hogan write_gc0_index(o_index); 333372582a6SJames Hogan 334372582a6SJames Hogan clear_root_gid(); 335372582a6SJames Hogan htw_start(); 336372582a6SJames Hogan local_irq_restore(flags); 337372582a6SJames Hogan return -EFAULT; 338372582a6SJames Hogan } 339372582a6SJames Hogan 340372582a6SJames Hogan /* Match! read the TLB entry */ 341372582a6SJames Hogan o_entrylo[0] = read_gc0_entrylo0(); 342372582a6SJames Hogan o_entrylo[1] = read_gc0_entrylo1(); 343372582a6SJames Hogan o_pagemask = read_gc0_pagemask(); 344372582a6SJames Hogan 345372582a6SJames Hogan mtc0_tlbr_hazard(); 346372582a6SJames Hogan guest_tlb_read(); 347372582a6SJames Hogan tlb_read_hazard(); 348372582a6SJames Hogan 349372582a6SJames Hogan entrylo[0] = read_gc0_entrylo0(); 350372582a6SJames Hogan entrylo[1] = read_gc0_entrylo1(); 351372582a6SJames Hogan pagemask = ~read_gc0_pagemask() & ~0x1fffl; 352372582a6SJames Hogan 353372582a6SJames Hogan write_gc0_entryhi(o_entryhi); 354372582a6SJames Hogan write_gc0_index(o_index); 355372582a6SJames Hogan write_gc0_entrylo0(o_entrylo[0]); 356372582a6SJames Hogan write_gc0_entrylo1(o_entrylo[1]); 357372582a6SJames Hogan write_gc0_pagemask(o_pagemask); 358372582a6SJames Hogan 359372582a6SJames Hogan clear_root_gid(); 360372582a6SJames Hogan htw_start(); 361372582a6SJames Hogan local_irq_restore(flags); 362372582a6SJames Hogan 363372582a6SJames Hogan /* Select one of the EntryLo values and interpret the GPA */ 364372582a6SJames Hogan pagemaskbit = (pagemask ^ (pagemask & (pagemask - 1))) >> 1; 365372582a6SJames Hogan pa = entrylo[!!(gva & pagemaskbit)]; 366372582a6SJames Hogan 367372582a6SJames Hogan /* 368372582a6SJames Hogan * TLB entry may have become invalid since TLB probe if physical FTLB 369372582a6SJames Hogan * entries are shared between threads (e.g. I6400). 370372582a6SJames Hogan */ 371372582a6SJames Hogan if (!(pa & ENTRYLO_V)) 372372582a6SJames Hogan return -EFAULT; 373372582a6SJames Hogan 374372582a6SJames Hogan /* 375372582a6SJames Hogan * Note, this doesn't take guest MIPS32 XPA into account, where PFN is 376372582a6SJames Hogan * split with XI/RI in the middle. 377372582a6SJames Hogan */ 378372582a6SJames Hogan pa = (pa << 6) & ~0xfffl; 379372582a6SJames Hogan pa |= gva & ~(pagemask | pagemaskbit); 380372582a6SJames Hogan 381372582a6SJames Hogan *gpa = pa; 382372582a6SJames Hogan return 0; 383372582a6SJames Hogan } 384372582a6SJames Hogan EXPORT_SYMBOL_GPL(kvm_vz_guest_tlb_lookup); 385372582a6SJames Hogan 386372582a6SJames Hogan /** 387372582a6SJames Hogan * kvm_vz_local_flush_roottlb_all_guests() - Flush all root TLB entries for 388372582a6SJames Hogan * guests. 389372582a6SJames Hogan * 390372582a6SJames Hogan * Invalidate all entries in root tlb which are GPA mappings. 391372582a6SJames Hogan */ 392372582a6SJames Hogan void kvm_vz_local_flush_roottlb_all_guests(void) 393372582a6SJames Hogan { 394372582a6SJames Hogan unsigned long flags; 395372582a6SJames Hogan unsigned long old_entryhi, old_pagemask, old_guestctl1; 396372582a6SJames Hogan int entry; 397372582a6SJames Hogan 398372582a6SJames Hogan if (WARN_ON(!cpu_has_guestid)) 399372582a6SJames Hogan return; 400372582a6SJames Hogan 401372582a6SJames Hogan local_irq_save(flags); 402372582a6SJames Hogan htw_stop(); 403372582a6SJames Hogan 404372582a6SJames Hogan /* TLBR may clobber EntryHi.ASID, PageMask, and GuestCtl1.RID */ 405372582a6SJames Hogan old_entryhi = read_c0_entryhi(); 406372582a6SJames Hogan old_pagemask = read_c0_pagemask(); 407372582a6SJames Hogan old_guestctl1 = read_c0_guestctl1(); 408372582a6SJames Hogan 409372582a6SJames Hogan /* 410372582a6SJames Hogan * Invalidate guest entries in root TLB while leaving root entries 411372582a6SJames Hogan * intact when possible. 412372582a6SJames Hogan */ 413372582a6SJames Hogan for (entry = 0; entry < current_cpu_data.tlbsize; entry++) { 414372582a6SJames Hogan write_c0_index(entry); 415372582a6SJames Hogan mtc0_tlbw_hazard(); 416372582a6SJames Hogan tlb_read(); 417372582a6SJames Hogan tlb_read_hazard(); 418372582a6SJames Hogan 419372582a6SJames Hogan /* Don't invalidate non-guest (RVA) mappings in the root TLB */ 420372582a6SJames Hogan if (!(read_c0_guestctl1() & MIPS_GCTL1_RID)) 421372582a6SJames Hogan continue; 422372582a6SJames Hogan 423372582a6SJames Hogan /* Make sure all entries differ. */ 424372582a6SJames Hogan write_c0_entryhi(UNIQUE_ENTRYHI(entry)); 425372582a6SJames Hogan write_c0_entrylo0(0); 426372582a6SJames Hogan write_c0_entrylo1(0); 427372582a6SJames Hogan write_c0_guestctl1(0); 428372582a6SJames Hogan mtc0_tlbw_hazard(); 429372582a6SJames Hogan tlb_write_indexed(); 430372582a6SJames Hogan } 431372582a6SJames Hogan 432372582a6SJames Hogan write_c0_entryhi(old_entryhi); 433372582a6SJames Hogan write_c0_pagemask(old_pagemask); 434372582a6SJames Hogan write_c0_guestctl1(old_guestctl1); 435372582a6SJames Hogan tlbw_use_hazard(); 436372582a6SJames Hogan 437372582a6SJames Hogan htw_start(); 438372582a6SJames Hogan local_irq_restore(flags); 439372582a6SJames Hogan } 440372582a6SJames Hogan EXPORT_SYMBOL_GPL(kvm_vz_local_flush_roottlb_all_guests); 441372582a6SJames Hogan 442372582a6SJames Hogan /** 443372582a6SJames Hogan * kvm_vz_local_flush_guesttlb_all() - Flush all guest TLB entries. 444372582a6SJames Hogan * 445372582a6SJames Hogan * Invalidate all entries in guest tlb irrespective of guestid. 446372582a6SJames Hogan */ 447372582a6SJames Hogan void kvm_vz_local_flush_guesttlb_all(void) 448372582a6SJames Hogan { 449372582a6SJames Hogan unsigned long flags; 450372582a6SJames Hogan unsigned long old_index; 451372582a6SJames Hogan unsigned long old_entryhi; 452372582a6SJames Hogan unsigned long old_entrylo[2]; 453372582a6SJames Hogan unsigned long old_pagemask; 454372582a6SJames Hogan int entry; 455824533adSJames Hogan u64 cvmmemctl2 = 0; 456372582a6SJames Hogan 457372582a6SJames Hogan local_irq_save(flags); 458372582a6SJames Hogan 459372582a6SJames Hogan /* Preserve all clobbered guest registers */ 460372582a6SJames Hogan old_index = read_gc0_index(); 461372582a6SJames Hogan old_entryhi = read_gc0_entryhi(); 462372582a6SJames Hogan old_entrylo[0] = read_gc0_entrylo0(); 463372582a6SJames Hogan old_entrylo[1] = read_gc0_entrylo1(); 464372582a6SJames Hogan old_pagemask = read_gc0_pagemask(); 465372582a6SJames Hogan 466824533adSJames Hogan switch (current_cpu_type()) { 467824533adSJames Hogan case CPU_CAVIUM_OCTEON3: 468824533adSJames Hogan /* Inhibit machine check due to multiple matching TLB entries */ 469824533adSJames Hogan cvmmemctl2 = read_c0_cvmmemctl2(); 470824533adSJames Hogan cvmmemctl2 |= CVMMEMCTL2_INHIBITTS; 471824533adSJames Hogan write_c0_cvmmemctl2(cvmmemctl2); 472824533adSJames Hogan break; 4737ff1f626SJason Yan } 474824533adSJames Hogan 475372582a6SJames Hogan /* Invalidate guest entries in guest TLB */ 476372582a6SJames Hogan write_gc0_entrylo0(0); 477372582a6SJames Hogan write_gc0_entrylo1(0); 478372582a6SJames Hogan write_gc0_pagemask(0); 479372582a6SJames Hogan for (entry = 0; entry < current_cpu_data.guest.tlbsize; entry++) { 480372582a6SJames Hogan /* Make sure all entries differ. */ 481372582a6SJames Hogan write_gc0_index(entry); 482372582a6SJames Hogan write_gc0_entryhi(UNIQUE_GUEST_ENTRYHI(entry)); 483372582a6SJames Hogan mtc0_tlbw_hazard(); 484372582a6SJames Hogan guest_tlb_write_indexed(); 485372582a6SJames Hogan } 486824533adSJames Hogan 487824533adSJames Hogan if (cvmmemctl2) { 488824533adSJames Hogan cvmmemctl2 &= ~CVMMEMCTL2_INHIBITTS; 489824533adSJames Hogan write_c0_cvmmemctl2(cvmmemctl2); 4907ff1f626SJason Yan } 491824533adSJames Hogan 492372582a6SJames Hogan write_gc0_index(old_index); 493372582a6SJames Hogan write_gc0_entryhi(old_entryhi); 494372582a6SJames Hogan write_gc0_entrylo0(old_entrylo[0]); 495372582a6SJames Hogan write_gc0_entrylo1(old_entrylo[1]); 496372582a6SJames Hogan write_gc0_pagemask(old_pagemask); 497372582a6SJames Hogan tlbw_use_hazard(); 498372582a6SJames Hogan 499372582a6SJames Hogan local_irq_restore(flags); 500372582a6SJames Hogan } 501372582a6SJames Hogan EXPORT_SYMBOL_GPL(kvm_vz_local_flush_guesttlb_all); 502372582a6SJames Hogan 503372582a6SJames Hogan /** 504372582a6SJames Hogan * kvm_vz_save_guesttlb() - Save a range of guest TLB entries. 505372582a6SJames Hogan * @buf: Buffer to write TLB entries into. 506372582a6SJames Hogan * @index: Start index. 507372582a6SJames Hogan * @count: Number of entries to save. 508372582a6SJames Hogan * 509372582a6SJames Hogan * Save a range of guest TLB entries. The caller must ensure interrupts are 510372582a6SJames Hogan * disabled. 511372582a6SJames Hogan */ 512372582a6SJames Hogan void kvm_vz_save_guesttlb(struct kvm_mips_tlb *buf, unsigned int index, 513372582a6SJames Hogan unsigned int count) 514372582a6SJames Hogan { 515372582a6SJames Hogan unsigned int end = index + count; 516372582a6SJames Hogan unsigned long old_entryhi, old_entrylo0, old_entrylo1, old_pagemask; 517372582a6SJames Hogan unsigned int guestctl1 = 0; 518372582a6SJames Hogan int old_index, i; 519372582a6SJames Hogan 520372582a6SJames Hogan /* Save registers we're about to clobber */ 521372582a6SJames Hogan old_index = read_gc0_index(); 522372582a6SJames Hogan old_entryhi = read_gc0_entryhi(); 523372582a6SJames Hogan old_entrylo0 = read_gc0_entrylo0(); 524372582a6SJames Hogan old_entrylo1 = read_gc0_entrylo1(); 525372582a6SJames Hogan old_pagemask = read_gc0_pagemask(); 526372582a6SJames Hogan 527372582a6SJames Hogan /* Set root GuestID for root probe */ 528372582a6SJames Hogan htw_stop(); 529372582a6SJames Hogan set_root_gid_to_guest_gid(); 530372582a6SJames Hogan if (cpu_has_guestid) 531372582a6SJames Hogan guestctl1 = read_c0_guestctl1(); 532372582a6SJames Hogan 533372582a6SJames Hogan /* Read each entry from guest TLB */ 534372582a6SJames Hogan for (i = index; i < end; ++i, ++buf) { 535372582a6SJames Hogan write_gc0_index(i); 536372582a6SJames Hogan 537372582a6SJames Hogan mtc0_tlbr_hazard(); 538372582a6SJames Hogan guest_tlb_read(); 539372582a6SJames Hogan tlb_read_hazard(); 540372582a6SJames Hogan 541372582a6SJames Hogan if (cpu_has_guestid && 542372582a6SJames Hogan (read_c0_guestctl1() ^ guestctl1) & MIPS_GCTL1_RID) { 543372582a6SJames Hogan /* Entry invalid or belongs to another guest */ 544372582a6SJames Hogan buf->tlb_hi = UNIQUE_GUEST_ENTRYHI(i); 545372582a6SJames Hogan buf->tlb_lo[0] = 0; 546372582a6SJames Hogan buf->tlb_lo[1] = 0; 547372582a6SJames Hogan buf->tlb_mask = 0; 548372582a6SJames Hogan } else { 549372582a6SJames Hogan /* Entry belongs to the right guest */ 550372582a6SJames Hogan buf->tlb_hi = read_gc0_entryhi(); 551372582a6SJames Hogan buf->tlb_lo[0] = read_gc0_entrylo0(); 552372582a6SJames Hogan buf->tlb_lo[1] = read_gc0_entrylo1(); 553372582a6SJames Hogan buf->tlb_mask = read_gc0_pagemask(); 554372582a6SJames Hogan } 555372582a6SJames Hogan } 556372582a6SJames Hogan 557372582a6SJames Hogan /* Clear root GuestID again */ 558372582a6SJames Hogan clear_root_gid(); 559372582a6SJames Hogan htw_start(); 560372582a6SJames Hogan 561372582a6SJames Hogan /* Restore clobbered registers */ 562372582a6SJames Hogan write_gc0_index(old_index); 563372582a6SJames Hogan write_gc0_entryhi(old_entryhi); 564372582a6SJames Hogan write_gc0_entrylo0(old_entrylo0); 565372582a6SJames Hogan write_gc0_entrylo1(old_entrylo1); 566372582a6SJames Hogan write_gc0_pagemask(old_pagemask); 567372582a6SJames Hogan 568372582a6SJames Hogan tlbw_use_hazard(); 569372582a6SJames Hogan } 570372582a6SJames Hogan EXPORT_SYMBOL_GPL(kvm_vz_save_guesttlb); 571372582a6SJames Hogan 572372582a6SJames Hogan /** 573372582a6SJames Hogan * kvm_vz_load_guesttlb() - Save a range of guest TLB entries. 574372582a6SJames Hogan * @buf: Buffer to read TLB entries from. 575372582a6SJames Hogan * @index: Start index. 576372582a6SJames Hogan * @count: Number of entries to load. 577372582a6SJames Hogan * 578372582a6SJames Hogan * Load a range of guest TLB entries. The caller must ensure interrupts are 579372582a6SJames Hogan * disabled. 580372582a6SJames Hogan */ 581372582a6SJames Hogan void kvm_vz_load_guesttlb(const struct kvm_mips_tlb *buf, unsigned int index, 582372582a6SJames Hogan unsigned int count) 583372582a6SJames Hogan { 584372582a6SJames Hogan unsigned int end = index + count; 585372582a6SJames Hogan unsigned long old_entryhi, old_entrylo0, old_entrylo1, old_pagemask; 586372582a6SJames Hogan int old_index, i; 587372582a6SJames Hogan 588372582a6SJames Hogan /* Save registers we're about to clobber */ 589372582a6SJames Hogan old_index = read_gc0_index(); 590372582a6SJames Hogan old_entryhi = read_gc0_entryhi(); 591372582a6SJames Hogan old_entrylo0 = read_gc0_entrylo0(); 592372582a6SJames Hogan old_entrylo1 = read_gc0_entrylo1(); 593372582a6SJames Hogan old_pagemask = read_gc0_pagemask(); 594372582a6SJames Hogan 595372582a6SJames Hogan /* Set root GuestID for root probe */ 596372582a6SJames Hogan htw_stop(); 597372582a6SJames Hogan set_root_gid_to_guest_gid(); 598372582a6SJames Hogan 599372582a6SJames Hogan /* Write each entry to guest TLB */ 600372582a6SJames Hogan for (i = index; i < end; ++i, ++buf) { 601372582a6SJames Hogan write_gc0_index(i); 602372582a6SJames Hogan write_gc0_entryhi(buf->tlb_hi); 603372582a6SJames Hogan write_gc0_entrylo0(buf->tlb_lo[0]); 604372582a6SJames Hogan write_gc0_entrylo1(buf->tlb_lo[1]); 605372582a6SJames Hogan write_gc0_pagemask(buf->tlb_mask); 606372582a6SJames Hogan 607372582a6SJames Hogan mtc0_tlbw_hazard(); 608372582a6SJames Hogan guest_tlb_write_indexed(); 609372582a6SJames Hogan } 610372582a6SJames Hogan 611372582a6SJames Hogan /* Clear root GuestID again */ 612372582a6SJames Hogan clear_root_gid(); 613372582a6SJames Hogan htw_start(); 614372582a6SJames Hogan 615372582a6SJames Hogan /* Restore clobbered registers */ 616372582a6SJames Hogan write_gc0_index(old_index); 617372582a6SJames Hogan write_gc0_entryhi(old_entryhi); 618372582a6SJames Hogan write_gc0_entrylo0(old_entrylo0); 619372582a6SJames Hogan write_gc0_entrylo1(old_entrylo1); 620372582a6SJames Hogan write_gc0_pagemask(old_pagemask); 621372582a6SJames Hogan 622372582a6SJames Hogan tlbw_use_hazard(); 623372582a6SJames Hogan } 624372582a6SJames Hogan EXPORT_SYMBOL_GPL(kvm_vz_load_guesttlb); 625372582a6SJames Hogan 6268a5097eeSHuacai Chen #ifdef CONFIG_CPU_LOONGSON64 6278a5097eeSHuacai Chen void kvm_loongson_clear_guest_vtlb(void) 6288a5097eeSHuacai Chen { 6298a5097eeSHuacai Chen int idx = read_gc0_index(); 6308a5097eeSHuacai Chen 6318a5097eeSHuacai Chen /* Set root GuestID for root probe and write of guest TLB entry */ 6328a5097eeSHuacai Chen set_root_gid_to_guest_gid(); 6338a5097eeSHuacai Chen 6348a5097eeSHuacai Chen write_gc0_index(0); 6358a5097eeSHuacai Chen guest_tlbinvf(); 6368a5097eeSHuacai Chen write_gc0_index(idx); 6378a5097eeSHuacai Chen 6388a5097eeSHuacai Chen clear_root_gid(); 6398a5097eeSHuacai Chen set_c0_diag(LOONGSON_DIAG_ITLB | LOONGSON_DIAG_DTLB); 6408a5097eeSHuacai Chen } 6418a5097eeSHuacai Chen EXPORT_SYMBOL_GPL(kvm_loongson_clear_guest_vtlb); 6428a5097eeSHuacai Chen 6438a5097eeSHuacai Chen void kvm_loongson_clear_guest_ftlb(void) 6448a5097eeSHuacai Chen { 6458a5097eeSHuacai Chen int i; 6468a5097eeSHuacai Chen int idx = read_gc0_index(); 6478a5097eeSHuacai Chen 6488a5097eeSHuacai Chen /* Set root GuestID for root probe and write of guest TLB entry */ 6498a5097eeSHuacai Chen set_root_gid_to_guest_gid(); 6508a5097eeSHuacai Chen 6518a5097eeSHuacai Chen for (i = current_cpu_data.tlbsizevtlb; 6528a5097eeSHuacai Chen i < (current_cpu_data.tlbsizevtlb + 6538a5097eeSHuacai Chen current_cpu_data.tlbsizeftlbsets); 6548a5097eeSHuacai Chen i++) { 6558a5097eeSHuacai Chen write_gc0_index(i); 6568a5097eeSHuacai Chen guest_tlbinvf(); 6578a5097eeSHuacai Chen } 6588a5097eeSHuacai Chen write_gc0_index(idx); 6598a5097eeSHuacai Chen 6608a5097eeSHuacai Chen clear_root_gid(); 6618a5097eeSHuacai Chen set_c0_diag(LOONGSON_DIAG_ITLB | LOONGSON_DIAG_DTLB); 6628a5097eeSHuacai Chen } 6638a5097eeSHuacai Chen EXPORT_SYMBOL_GPL(kvm_loongson_clear_guest_ftlb); 6648a5097eeSHuacai Chen #endif 6658a5097eeSHuacai Chen 666372582a6SJames Hogan #endif 667372582a6SJames Hogan 668a7ebb2e4SJames Hogan /** 669a7ebb2e4SJames Hogan * kvm_mips_suspend_mm() - Suspend the active mm. 670a7ebb2e4SJames Hogan * @cpu The CPU we're running on. 671a7ebb2e4SJames Hogan * 672a7ebb2e4SJames Hogan * Suspend the active_mm, ready for a switch to a KVM guest virtual address 673a7ebb2e4SJames Hogan * space. This is left active for the duration of guest context, including time 674a7ebb2e4SJames Hogan * with interrupts enabled, so we need to be careful not to confuse e.g. cache 675a7ebb2e4SJames Hogan * management IPIs. 676a7ebb2e4SJames Hogan * 677a7ebb2e4SJames Hogan * kvm_mips_resume_mm() should be called before context switching to a different 678a7ebb2e4SJames Hogan * process so we don't need to worry about reference counting. 679a7ebb2e4SJames Hogan * 680a7ebb2e4SJames Hogan * This needs to be in static kernel code to avoid exporting init_mm. 681a7ebb2e4SJames Hogan */ 682a7ebb2e4SJames Hogan void kvm_mips_suspend_mm(int cpu) 683a7ebb2e4SJames Hogan { 684a7ebb2e4SJames Hogan cpumask_clear_cpu(cpu, mm_cpumask(current->active_mm)); 685a7ebb2e4SJames Hogan current->active_mm = &init_mm; 686a7ebb2e4SJames Hogan } 687a7ebb2e4SJames Hogan EXPORT_SYMBOL_GPL(kvm_mips_suspend_mm); 688a7ebb2e4SJames Hogan 689a7ebb2e4SJames Hogan /** 690a7ebb2e4SJames Hogan * kvm_mips_resume_mm() - Resume the current process mm. 691a7ebb2e4SJames Hogan * @cpu The CPU we're running on. 692a7ebb2e4SJames Hogan * 693a7ebb2e4SJames Hogan * Resume the mm of the current process, after a switch back from a KVM guest 694a7ebb2e4SJames Hogan * virtual address space (see kvm_mips_suspend_mm()). 695a7ebb2e4SJames Hogan */ 696a7ebb2e4SJames Hogan void kvm_mips_resume_mm(int cpu) 697a7ebb2e4SJames Hogan { 698a7ebb2e4SJames Hogan cpumask_set_cpu(cpu, mm_cpumask(current->mm)); 699a7ebb2e4SJames Hogan current->active_mm = current->mm; 700a7ebb2e4SJames Hogan } 701a7ebb2e4SJames Hogan EXPORT_SYMBOL_GPL(kvm_mips_resume_mm); 702