1*d7d5b05fSDeng-Cheng Zhu /* 2*d7d5b05fSDeng-Cheng Zhu * This file is subject to the terms and conditions of the GNU General Public 3*d7d5b05fSDeng-Cheng Zhu * License. See the file "COPYING" in the main directory of this archive 4*d7d5b05fSDeng-Cheng Zhu * for more details. 5*d7d5b05fSDeng-Cheng Zhu * 6*d7d5b05fSDeng-Cheng Zhu * KVM/MIPS TLB handling, this file is part of the Linux host kernel so that 7*d7d5b05fSDeng-Cheng Zhu * TLB handlers run from KSEG0 8*d7d5b05fSDeng-Cheng Zhu * 9*d7d5b05fSDeng-Cheng Zhu * Copyright (C) 2012 MIPS Technologies, Inc. All rights reserved. 10*d7d5b05fSDeng-Cheng Zhu * Authors: Sanjay Lal <sanjayl@kymasys.com> 11*d7d5b05fSDeng-Cheng Zhu */ 12*d7d5b05fSDeng-Cheng Zhu 13*d7d5b05fSDeng-Cheng Zhu #include <linux/sched.h> 14*d7d5b05fSDeng-Cheng Zhu #include <linux/smp.h> 15*d7d5b05fSDeng-Cheng Zhu #include <linux/mm.h> 16*d7d5b05fSDeng-Cheng Zhu #include <linux/delay.h> 17*d7d5b05fSDeng-Cheng Zhu #include <linux/module.h> 18*d7d5b05fSDeng-Cheng Zhu #include <linux/kvm_host.h> 19*d7d5b05fSDeng-Cheng Zhu #include <linux/srcu.h> 20*d7d5b05fSDeng-Cheng Zhu 21*d7d5b05fSDeng-Cheng Zhu #include <asm/cpu.h> 22*d7d5b05fSDeng-Cheng Zhu #include <asm/bootinfo.h> 23*d7d5b05fSDeng-Cheng Zhu #include <asm/mmu_context.h> 24*d7d5b05fSDeng-Cheng Zhu #include <asm/pgtable.h> 25*d7d5b05fSDeng-Cheng Zhu #include <asm/cacheflush.h> 26*d7d5b05fSDeng-Cheng Zhu #include <asm/tlb.h> 27*d7d5b05fSDeng-Cheng Zhu 28*d7d5b05fSDeng-Cheng Zhu #undef CONFIG_MIPS_MT 29*d7d5b05fSDeng-Cheng Zhu #include <asm/r4kcache.h> 30*d7d5b05fSDeng-Cheng Zhu #define CONFIG_MIPS_MT 31*d7d5b05fSDeng-Cheng Zhu 32*d7d5b05fSDeng-Cheng Zhu #define KVM_GUEST_PC_TLB 0 33*d7d5b05fSDeng-Cheng Zhu #define KVM_GUEST_SP_TLB 1 34*d7d5b05fSDeng-Cheng Zhu 35*d7d5b05fSDeng-Cheng Zhu #define PRIx64 "llx" 36*d7d5b05fSDeng-Cheng Zhu 37*d7d5b05fSDeng-Cheng Zhu atomic_t kvm_mips_instance; 38*d7d5b05fSDeng-Cheng Zhu EXPORT_SYMBOL(kvm_mips_instance); 39*d7d5b05fSDeng-Cheng Zhu 40*d7d5b05fSDeng-Cheng Zhu /* These function pointers are initialized once the KVM module is loaded */ 41*d7d5b05fSDeng-Cheng Zhu pfn_t (*kvm_mips_gfn_to_pfn)(struct kvm *kvm, gfn_t gfn); 42*d7d5b05fSDeng-Cheng Zhu EXPORT_SYMBOL(kvm_mips_gfn_to_pfn); 43*d7d5b05fSDeng-Cheng Zhu 44*d7d5b05fSDeng-Cheng Zhu void (*kvm_mips_release_pfn_clean)(pfn_t pfn); 45*d7d5b05fSDeng-Cheng Zhu EXPORT_SYMBOL(kvm_mips_release_pfn_clean); 46*d7d5b05fSDeng-Cheng Zhu 47*d7d5b05fSDeng-Cheng Zhu bool (*kvm_mips_is_error_pfn)(pfn_t pfn); 48*d7d5b05fSDeng-Cheng Zhu EXPORT_SYMBOL(kvm_mips_is_error_pfn); 49*d7d5b05fSDeng-Cheng Zhu 50*d7d5b05fSDeng-Cheng Zhu uint32_t kvm_mips_get_kernel_asid(struct kvm_vcpu *vcpu) 51*d7d5b05fSDeng-Cheng Zhu { 52*d7d5b05fSDeng-Cheng Zhu return vcpu->arch.guest_kernel_asid[smp_processor_id()] & ASID_MASK; 53*d7d5b05fSDeng-Cheng Zhu } 54*d7d5b05fSDeng-Cheng Zhu 55*d7d5b05fSDeng-Cheng Zhu uint32_t kvm_mips_get_user_asid(struct kvm_vcpu *vcpu) 56*d7d5b05fSDeng-Cheng Zhu { 57*d7d5b05fSDeng-Cheng Zhu return vcpu->arch.guest_user_asid[smp_processor_id()] & ASID_MASK; 58*d7d5b05fSDeng-Cheng Zhu } 59*d7d5b05fSDeng-Cheng Zhu 60*d7d5b05fSDeng-Cheng Zhu inline uint32_t kvm_mips_get_commpage_asid(struct kvm_vcpu *vcpu) 61*d7d5b05fSDeng-Cheng Zhu { 62*d7d5b05fSDeng-Cheng Zhu return vcpu->kvm->arch.commpage_tlb; 63*d7d5b05fSDeng-Cheng Zhu } 64*d7d5b05fSDeng-Cheng Zhu 65*d7d5b05fSDeng-Cheng Zhu /* Structure defining an tlb entry data set. */ 66*d7d5b05fSDeng-Cheng Zhu 67*d7d5b05fSDeng-Cheng Zhu void kvm_mips_dump_host_tlbs(void) 68*d7d5b05fSDeng-Cheng Zhu { 69*d7d5b05fSDeng-Cheng Zhu unsigned long old_entryhi; 70*d7d5b05fSDeng-Cheng Zhu unsigned long old_pagemask; 71*d7d5b05fSDeng-Cheng Zhu struct kvm_mips_tlb tlb; 72*d7d5b05fSDeng-Cheng Zhu unsigned long flags; 73*d7d5b05fSDeng-Cheng Zhu int i; 74*d7d5b05fSDeng-Cheng Zhu 75*d7d5b05fSDeng-Cheng Zhu local_irq_save(flags); 76*d7d5b05fSDeng-Cheng Zhu 77*d7d5b05fSDeng-Cheng Zhu old_entryhi = read_c0_entryhi(); 78*d7d5b05fSDeng-Cheng Zhu old_pagemask = read_c0_pagemask(); 79*d7d5b05fSDeng-Cheng Zhu 80*d7d5b05fSDeng-Cheng Zhu kvm_info("HOST TLBs:\n"); 81*d7d5b05fSDeng-Cheng Zhu kvm_info("ASID: %#lx\n", read_c0_entryhi() & ASID_MASK); 82*d7d5b05fSDeng-Cheng Zhu 83*d7d5b05fSDeng-Cheng Zhu for (i = 0; i < current_cpu_data.tlbsize; i++) { 84*d7d5b05fSDeng-Cheng Zhu write_c0_index(i); 85*d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 86*d7d5b05fSDeng-Cheng Zhu 87*d7d5b05fSDeng-Cheng Zhu tlb_read(); 88*d7d5b05fSDeng-Cheng Zhu tlbw_use_hazard(); 89*d7d5b05fSDeng-Cheng Zhu 90*d7d5b05fSDeng-Cheng Zhu tlb.tlb_hi = read_c0_entryhi(); 91*d7d5b05fSDeng-Cheng Zhu tlb.tlb_lo0 = read_c0_entrylo0(); 92*d7d5b05fSDeng-Cheng Zhu tlb.tlb_lo1 = read_c0_entrylo1(); 93*d7d5b05fSDeng-Cheng Zhu tlb.tlb_mask = read_c0_pagemask(); 94*d7d5b05fSDeng-Cheng Zhu 95*d7d5b05fSDeng-Cheng Zhu kvm_info("TLB%c%3d Hi 0x%08lx ", 96*d7d5b05fSDeng-Cheng Zhu (tlb.tlb_lo0 | tlb.tlb_lo1) & MIPS3_PG_V ? ' ' : '*', 97*d7d5b05fSDeng-Cheng Zhu i, tlb.tlb_hi); 98*d7d5b05fSDeng-Cheng Zhu kvm_info("Lo0=0x%09" PRIx64 " %c%c attr %lx ", 99*d7d5b05fSDeng-Cheng Zhu (uint64_t) mips3_tlbpfn_to_paddr(tlb.tlb_lo0), 100*d7d5b05fSDeng-Cheng Zhu (tlb.tlb_lo0 & MIPS3_PG_D) ? 'D' : ' ', 101*d7d5b05fSDeng-Cheng Zhu (tlb.tlb_lo0 & MIPS3_PG_G) ? 'G' : ' ', 102*d7d5b05fSDeng-Cheng Zhu (tlb.tlb_lo0 >> 3) & 7); 103*d7d5b05fSDeng-Cheng Zhu kvm_info("Lo1=0x%09" PRIx64 " %c%c attr %lx sz=%lx\n", 104*d7d5b05fSDeng-Cheng Zhu (uint64_t) mips3_tlbpfn_to_paddr(tlb.tlb_lo1), 105*d7d5b05fSDeng-Cheng Zhu (tlb.tlb_lo1 & MIPS3_PG_D) ? 'D' : ' ', 106*d7d5b05fSDeng-Cheng Zhu (tlb.tlb_lo1 & MIPS3_PG_G) ? 'G' : ' ', 107*d7d5b05fSDeng-Cheng Zhu (tlb.tlb_lo1 >> 3) & 7, tlb.tlb_mask); 108*d7d5b05fSDeng-Cheng Zhu } 109*d7d5b05fSDeng-Cheng Zhu write_c0_entryhi(old_entryhi); 110*d7d5b05fSDeng-Cheng Zhu write_c0_pagemask(old_pagemask); 111*d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 112*d7d5b05fSDeng-Cheng Zhu local_irq_restore(flags); 113*d7d5b05fSDeng-Cheng Zhu } 114*d7d5b05fSDeng-Cheng Zhu EXPORT_SYMBOL(kvm_mips_dump_host_tlbs); 115*d7d5b05fSDeng-Cheng Zhu 116*d7d5b05fSDeng-Cheng Zhu void kvm_mips_dump_guest_tlbs(struct kvm_vcpu *vcpu) 117*d7d5b05fSDeng-Cheng Zhu { 118*d7d5b05fSDeng-Cheng Zhu struct mips_coproc *cop0 = vcpu->arch.cop0; 119*d7d5b05fSDeng-Cheng Zhu struct kvm_mips_tlb tlb; 120*d7d5b05fSDeng-Cheng Zhu int i; 121*d7d5b05fSDeng-Cheng Zhu 122*d7d5b05fSDeng-Cheng Zhu kvm_info("Guest TLBs:\n"); 123*d7d5b05fSDeng-Cheng Zhu kvm_info("Guest EntryHi: %#lx\n", kvm_read_c0_guest_entryhi(cop0)); 124*d7d5b05fSDeng-Cheng Zhu 125*d7d5b05fSDeng-Cheng Zhu for (i = 0; i < KVM_MIPS_GUEST_TLB_SIZE; i++) { 126*d7d5b05fSDeng-Cheng Zhu tlb = vcpu->arch.guest_tlb[i]; 127*d7d5b05fSDeng-Cheng Zhu kvm_info("TLB%c%3d Hi 0x%08lx ", 128*d7d5b05fSDeng-Cheng Zhu (tlb.tlb_lo0 | tlb.tlb_lo1) & MIPS3_PG_V ? ' ' : '*', 129*d7d5b05fSDeng-Cheng Zhu i, tlb.tlb_hi); 130*d7d5b05fSDeng-Cheng Zhu kvm_info("Lo0=0x%09" PRIx64 " %c%c attr %lx ", 131*d7d5b05fSDeng-Cheng Zhu (uint64_t) mips3_tlbpfn_to_paddr(tlb.tlb_lo0), 132*d7d5b05fSDeng-Cheng Zhu (tlb.tlb_lo0 & MIPS3_PG_D) ? 'D' : ' ', 133*d7d5b05fSDeng-Cheng Zhu (tlb.tlb_lo0 & MIPS3_PG_G) ? 'G' : ' ', 134*d7d5b05fSDeng-Cheng Zhu (tlb.tlb_lo0 >> 3) & 7); 135*d7d5b05fSDeng-Cheng Zhu kvm_info("Lo1=0x%09" PRIx64 " %c%c attr %lx sz=%lx\n", 136*d7d5b05fSDeng-Cheng Zhu (uint64_t) mips3_tlbpfn_to_paddr(tlb.tlb_lo1), 137*d7d5b05fSDeng-Cheng Zhu (tlb.tlb_lo1 & MIPS3_PG_D) ? 'D' : ' ', 138*d7d5b05fSDeng-Cheng Zhu (tlb.tlb_lo1 & MIPS3_PG_G) ? 'G' : ' ', 139*d7d5b05fSDeng-Cheng Zhu (tlb.tlb_lo1 >> 3) & 7, tlb.tlb_mask); 140*d7d5b05fSDeng-Cheng Zhu } 141*d7d5b05fSDeng-Cheng Zhu } 142*d7d5b05fSDeng-Cheng Zhu EXPORT_SYMBOL(kvm_mips_dump_guest_tlbs); 143*d7d5b05fSDeng-Cheng Zhu 144*d7d5b05fSDeng-Cheng Zhu static int kvm_mips_map_page(struct kvm *kvm, gfn_t gfn) 145*d7d5b05fSDeng-Cheng Zhu { 146*d7d5b05fSDeng-Cheng Zhu int srcu_idx, err = 0; 147*d7d5b05fSDeng-Cheng Zhu pfn_t pfn; 148*d7d5b05fSDeng-Cheng Zhu 149*d7d5b05fSDeng-Cheng Zhu if (kvm->arch.guest_pmap[gfn] != KVM_INVALID_PAGE) 150*d7d5b05fSDeng-Cheng Zhu return 0; 151*d7d5b05fSDeng-Cheng Zhu 152*d7d5b05fSDeng-Cheng Zhu srcu_idx = srcu_read_lock(&kvm->srcu); 153*d7d5b05fSDeng-Cheng Zhu pfn = kvm_mips_gfn_to_pfn(kvm, gfn); 154*d7d5b05fSDeng-Cheng Zhu 155*d7d5b05fSDeng-Cheng Zhu if (kvm_mips_is_error_pfn(pfn)) { 156*d7d5b05fSDeng-Cheng Zhu kvm_err("Couldn't get pfn for gfn %#" PRIx64 "!\n", gfn); 157*d7d5b05fSDeng-Cheng Zhu err = -EFAULT; 158*d7d5b05fSDeng-Cheng Zhu goto out; 159*d7d5b05fSDeng-Cheng Zhu } 160*d7d5b05fSDeng-Cheng Zhu 161*d7d5b05fSDeng-Cheng Zhu kvm->arch.guest_pmap[gfn] = pfn; 162*d7d5b05fSDeng-Cheng Zhu out: 163*d7d5b05fSDeng-Cheng Zhu srcu_read_unlock(&kvm->srcu, srcu_idx); 164*d7d5b05fSDeng-Cheng Zhu return err; 165*d7d5b05fSDeng-Cheng Zhu } 166*d7d5b05fSDeng-Cheng Zhu 167*d7d5b05fSDeng-Cheng Zhu /* Translate guest KSEG0 addresses to Host PA */ 168*d7d5b05fSDeng-Cheng Zhu unsigned long kvm_mips_translate_guest_kseg0_to_hpa(struct kvm_vcpu *vcpu, 169*d7d5b05fSDeng-Cheng Zhu unsigned long gva) 170*d7d5b05fSDeng-Cheng Zhu { 171*d7d5b05fSDeng-Cheng Zhu gfn_t gfn; 172*d7d5b05fSDeng-Cheng Zhu uint32_t offset = gva & ~PAGE_MASK; 173*d7d5b05fSDeng-Cheng Zhu struct kvm *kvm = vcpu->kvm; 174*d7d5b05fSDeng-Cheng Zhu 175*d7d5b05fSDeng-Cheng Zhu if (KVM_GUEST_KSEGX(gva) != KVM_GUEST_KSEG0) { 176*d7d5b05fSDeng-Cheng Zhu kvm_err("%s/%p: Invalid gva: %#lx\n", __func__, 177*d7d5b05fSDeng-Cheng Zhu __builtin_return_address(0), gva); 178*d7d5b05fSDeng-Cheng Zhu return KVM_INVALID_PAGE; 179*d7d5b05fSDeng-Cheng Zhu } 180*d7d5b05fSDeng-Cheng Zhu 181*d7d5b05fSDeng-Cheng Zhu gfn = (KVM_GUEST_CPHYSADDR(gva) >> PAGE_SHIFT); 182*d7d5b05fSDeng-Cheng Zhu 183*d7d5b05fSDeng-Cheng Zhu if (gfn >= kvm->arch.guest_pmap_npages) { 184*d7d5b05fSDeng-Cheng Zhu kvm_err("%s: Invalid gfn: %#llx, GVA: %#lx\n", __func__, gfn, 185*d7d5b05fSDeng-Cheng Zhu gva); 186*d7d5b05fSDeng-Cheng Zhu return KVM_INVALID_PAGE; 187*d7d5b05fSDeng-Cheng Zhu } 188*d7d5b05fSDeng-Cheng Zhu 189*d7d5b05fSDeng-Cheng Zhu if (kvm_mips_map_page(vcpu->kvm, gfn) < 0) 190*d7d5b05fSDeng-Cheng Zhu return KVM_INVALID_ADDR; 191*d7d5b05fSDeng-Cheng Zhu 192*d7d5b05fSDeng-Cheng Zhu return (kvm->arch.guest_pmap[gfn] << PAGE_SHIFT) + offset; 193*d7d5b05fSDeng-Cheng Zhu } 194*d7d5b05fSDeng-Cheng Zhu EXPORT_SYMBOL(kvm_mips_translate_guest_kseg0_to_hpa); 195*d7d5b05fSDeng-Cheng Zhu 196*d7d5b05fSDeng-Cheng Zhu /* XXXKYMA: Must be called with interrupts disabled */ 197*d7d5b05fSDeng-Cheng Zhu /* set flush_dcache_mask == 0 if no dcache flush required */ 198*d7d5b05fSDeng-Cheng Zhu int kvm_mips_host_tlb_write(struct kvm_vcpu *vcpu, unsigned long entryhi, 199*d7d5b05fSDeng-Cheng Zhu unsigned long entrylo0, unsigned long entrylo1, 200*d7d5b05fSDeng-Cheng Zhu int flush_dcache_mask) 201*d7d5b05fSDeng-Cheng Zhu { 202*d7d5b05fSDeng-Cheng Zhu unsigned long flags; 203*d7d5b05fSDeng-Cheng Zhu unsigned long old_entryhi; 204*d7d5b05fSDeng-Cheng Zhu int idx; 205*d7d5b05fSDeng-Cheng Zhu 206*d7d5b05fSDeng-Cheng Zhu local_irq_save(flags); 207*d7d5b05fSDeng-Cheng Zhu 208*d7d5b05fSDeng-Cheng Zhu old_entryhi = read_c0_entryhi(); 209*d7d5b05fSDeng-Cheng Zhu write_c0_entryhi(entryhi); 210*d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 211*d7d5b05fSDeng-Cheng Zhu 212*d7d5b05fSDeng-Cheng Zhu tlb_probe(); 213*d7d5b05fSDeng-Cheng Zhu tlb_probe_hazard(); 214*d7d5b05fSDeng-Cheng Zhu idx = read_c0_index(); 215*d7d5b05fSDeng-Cheng Zhu 216*d7d5b05fSDeng-Cheng Zhu if (idx > current_cpu_data.tlbsize) { 217*d7d5b05fSDeng-Cheng Zhu kvm_err("%s: Invalid Index: %d\n", __func__, idx); 218*d7d5b05fSDeng-Cheng Zhu kvm_mips_dump_host_tlbs(); 219*d7d5b05fSDeng-Cheng Zhu return -1; 220*d7d5b05fSDeng-Cheng Zhu } 221*d7d5b05fSDeng-Cheng Zhu 222*d7d5b05fSDeng-Cheng Zhu write_c0_entrylo0(entrylo0); 223*d7d5b05fSDeng-Cheng Zhu write_c0_entrylo1(entrylo1); 224*d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 225*d7d5b05fSDeng-Cheng Zhu 226*d7d5b05fSDeng-Cheng Zhu if (idx < 0) 227*d7d5b05fSDeng-Cheng Zhu tlb_write_random(); 228*d7d5b05fSDeng-Cheng Zhu else 229*d7d5b05fSDeng-Cheng Zhu tlb_write_indexed(); 230*d7d5b05fSDeng-Cheng Zhu tlbw_use_hazard(); 231*d7d5b05fSDeng-Cheng Zhu 232*d7d5b05fSDeng-Cheng Zhu kvm_debug("@ %#lx idx: %2d [entryhi(R): %#lx] entrylo0(R): 0x%08lx, entrylo1(R): 0x%08lx\n", 233*d7d5b05fSDeng-Cheng Zhu vcpu->arch.pc, idx, read_c0_entryhi(), 234*d7d5b05fSDeng-Cheng Zhu read_c0_entrylo0(), read_c0_entrylo1()); 235*d7d5b05fSDeng-Cheng Zhu 236*d7d5b05fSDeng-Cheng Zhu /* Flush D-cache */ 237*d7d5b05fSDeng-Cheng Zhu if (flush_dcache_mask) { 238*d7d5b05fSDeng-Cheng Zhu if (entrylo0 & MIPS3_PG_V) { 239*d7d5b05fSDeng-Cheng Zhu ++vcpu->stat.flush_dcache_exits; 240*d7d5b05fSDeng-Cheng Zhu flush_data_cache_page((entryhi & VPN2_MASK) & 241*d7d5b05fSDeng-Cheng Zhu ~flush_dcache_mask); 242*d7d5b05fSDeng-Cheng Zhu } 243*d7d5b05fSDeng-Cheng Zhu if (entrylo1 & MIPS3_PG_V) { 244*d7d5b05fSDeng-Cheng Zhu ++vcpu->stat.flush_dcache_exits; 245*d7d5b05fSDeng-Cheng Zhu flush_data_cache_page(((entryhi & VPN2_MASK) & 246*d7d5b05fSDeng-Cheng Zhu ~flush_dcache_mask) | 247*d7d5b05fSDeng-Cheng Zhu (0x1 << PAGE_SHIFT)); 248*d7d5b05fSDeng-Cheng Zhu } 249*d7d5b05fSDeng-Cheng Zhu } 250*d7d5b05fSDeng-Cheng Zhu 251*d7d5b05fSDeng-Cheng Zhu /* Restore old ASID */ 252*d7d5b05fSDeng-Cheng Zhu write_c0_entryhi(old_entryhi); 253*d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 254*d7d5b05fSDeng-Cheng Zhu tlbw_use_hazard(); 255*d7d5b05fSDeng-Cheng Zhu local_irq_restore(flags); 256*d7d5b05fSDeng-Cheng Zhu return 0; 257*d7d5b05fSDeng-Cheng Zhu } 258*d7d5b05fSDeng-Cheng Zhu 259*d7d5b05fSDeng-Cheng Zhu /* XXXKYMA: Must be called with interrupts disabled */ 260*d7d5b05fSDeng-Cheng Zhu int kvm_mips_handle_kseg0_tlb_fault(unsigned long badvaddr, 261*d7d5b05fSDeng-Cheng Zhu struct kvm_vcpu *vcpu) 262*d7d5b05fSDeng-Cheng Zhu { 263*d7d5b05fSDeng-Cheng Zhu gfn_t gfn; 264*d7d5b05fSDeng-Cheng Zhu pfn_t pfn0, pfn1; 265*d7d5b05fSDeng-Cheng Zhu unsigned long vaddr = 0; 266*d7d5b05fSDeng-Cheng Zhu unsigned long entryhi = 0, entrylo0 = 0, entrylo1 = 0; 267*d7d5b05fSDeng-Cheng Zhu int even; 268*d7d5b05fSDeng-Cheng Zhu struct kvm *kvm = vcpu->kvm; 269*d7d5b05fSDeng-Cheng Zhu const int flush_dcache_mask = 0; 270*d7d5b05fSDeng-Cheng Zhu 271*d7d5b05fSDeng-Cheng Zhu if (KVM_GUEST_KSEGX(badvaddr) != KVM_GUEST_KSEG0) { 272*d7d5b05fSDeng-Cheng Zhu kvm_err("%s: Invalid BadVaddr: %#lx\n", __func__, badvaddr); 273*d7d5b05fSDeng-Cheng Zhu kvm_mips_dump_host_tlbs(); 274*d7d5b05fSDeng-Cheng Zhu return -1; 275*d7d5b05fSDeng-Cheng Zhu } 276*d7d5b05fSDeng-Cheng Zhu 277*d7d5b05fSDeng-Cheng Zhu gfn = (KVM_GUEST_CPHYSADDR(badvaddr) >> PAGE_SHIFT); 278*d7d5b05fSDeng-Cheng Zhu if (gfn >= kvm->arch.guest_pmap_npages) { 279*d7d5b05fSDeng-Cheng Zhu kvm_err("%s: Invalid gfn: %#llx, BadVaddr: %#lx\n", __func__, 280*d7d5b05fSDeng-Cheng Zhu gfn, badvaddr); 281*d7d5b05fSDeng-Cheng Zhu kvm_mips_dump_host_tlbs(); 282*d7d5b05fSDeng-Cheng Zhu return -1; 283*d7d5b05fSDeng-Cheng Zhu } 284*d7d5b05fSDeng-Cheng Zhu even = !(gfn & 0x1); 285*d7d5b05fSDeng-Cheng Zhu vaddr = badvaddr & (PAGE_MASK << 1); 286*d7d5b05fSDeng-Cheng Zhu 287*d7d5b05fSDeng-Cheng Zhu if (kvm_mips_map_page(vcpu->kvm, gfn) < 0) 288*d7d5b05fSDeng-Cheng Zhu return -1; 289*d7d5b05fSDeng-Cheng Zhu 290*d7d5b05fSDeng-Cheng Zhu if (kvm_mips_map_page(vcpu->kvm, gfn ^ 0x1) < 0) 291*d7d5b05fSDeng-Cheng Zhu return -1; 292*d7d5b05fSDeng-Cheng Zhu 293*d7d5b05fSDeng-Cheng Zhu if (even) { 294*d7d5b05fSDeng-Cheng Zhu pfn0 = kvm->arch.guest_pmap[gfn]; 295*d7d5b05fSDeng-Cheng Zhu pfn1 = kvm->arch.guest_pmap[gfn ^ 0x1]; 296*d7d5b05fSDeng-Cheng Zhu } else { 297*d7d5b05fSDeng-Cheng Zhu pfn0 = kvm->arch.guest_pmap[gfn ^ 0x1]; 298*d7d5b05fSDeng-Cheng Zhu pfn1 = kvm->arch.guest_pmap[gfn]; 299*d7d5b05fSDeng-Cheng Zhu } 300*d7d5b05fSDeng-Cheng Zhu 301*d7d5b05fSDeng-Cheng Zhu entryhi = (vaddr | kvm_mips_get_kernel_asid(vcpu)); 302*d7d5b05fSDeng-Cheng Zhu entrylo0 = mips3_paddr_to_tlbpfn(pfn0 << PAGE_SHIFT) | (0x3 << 3) | 303*d7d5b05fSDeng-Cheng Zhu (1 << 2) | (0x1 << 1); 304*d7d5b05fSDeng-Cheng Zhu entrylo1 = mips3_paddr_to_tlbpfn(pfn1 << PAGE_SHIFT) | (0x3 << 3) | 305*d7d5b05fSDeng-Cheng Zhu (1 << 2) | (0x1 << 1); 306*d7d5b05fSDeng-Cheng Zhu 307*d7d5b05fSDeng-Cheng Zhu return kvm_mips_host_tlb_write(vcpu, entryhi, entrylo0, entrylo1, 308*d7d5b05fSDeng-Cheng Zhu flush_dcache_mask); 309*d7d5b05fSDeng-Cheng Zhu } 310*d7d5b05fSDeng-Cheng Zhu EXPORT_SYMBOL(kvm_mips_handle_kseg0_tlb_fault); 311*d7d5b05fSDeng-Cheng Zhu 312*d7d5b05fSDeng-Cheng Zhu int kvm_mips_handle_commpage_tlb_fault(unsigned long badvaddr, 313*d7d5b05fSDeng-Cheng Zhu struct kvm_vcpu *vcpu) 314*d7d5b05fSDeng-Cheng Zhu { 315*d7d5b05fSDeng-Cheng Zhu pfn_t pfn0, pfn1; 316*d7d5b05fSDeng-Cheng Zhu unsigned long flags, old_entryhi = 0, vaddr = 0; 317*d7d5b05fSDeng-Cheng Zhu unsigned long entrylo0 = 0, entrylo1 = 0; 318*d7d5b05fSDeng-Cheng Zhu 319*d7d5b05fSDeng-Cheng Zhu pfn0 = CPHYSADDR(vcpu->arch.kseg0_commpage) >> PAGE_SHIFT; 320*d7d5b05fSDeng-Cheng Zhu pfn1 = 0; 321*d7d5b05fSDeng-Cheng Zhu entrylo0 = mips3_paddr_to_tlbpfn(pfn0 << PAGE_SHIFT) | (0x3 << 3) | 322*d7d5b05fSDeng-Cheng Zhu (1 << 2) | (0x1 << 1); 323*d7d5b05fSDeng-Cheng Zhu entrylo1 = 0; 324*d7d5b05fSDeng-Cheng Zhu 325*d7d5b05fSDeng-Cheng Zhu local_irq_save(flags); 326*d7d5b05fSDeng-Cheng Zhu 327*d7d5b05fSDeng-Cheng Zhu old_entryhi = read_c0_entryhi(); 328*d7d5b05fSDeng-Cheng Zhu vaddr = badvaddr & (PAGE_MASK << 1); 329*d7d5b05fSDeng-Cheng Zhu write_c0_entryhi(vaddr | kvm_mips_get_kernel_asid(vcpu)); 330*d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 331*d7d5b05fSDeng-Cheng Zhu write_c0_entrylo0(entrylo0); 332*d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 333*d7d5b05fSDeng-Cheng Zhu write_c0_entrylo1(entrylo1); 334*d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 335*d7d5b05fSDeng-Cheng Zhu write_c0_index(kvm_mips_get_commpage_asid(vcpu)); 336*d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 337*d7d5b05fSDeng-Cheng Zhu tlb_write_indexed(); 338*d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 339*d7d5b05fSDeng-Cheng Zhu tlbw_use_hazard(); 340*d7d5b05fSDeng-Cheng Zhu 341*d7d5b05fSDeng-Cheng Zhu kvm_debug("@ %#lx idx: %2d [entryhi(R): %#lx] entrylo0 (R): 0x%08lx, entrylo1(R): 0x%08lx\n", 342*d7d5b05fSDeng-Cheng Zhu vcpu->arch.pc, read_c0_index(), read_c0_entryhi(), 343*d7d5b05fSDeng-Cheng Zhu read_c0_entrylo0(), read_c0_entrylo1()); 344*d7d5b05fSDeng-Cheng Zhu 345*d7d5b05fSDeng-Cheng Zhu /* Restore old ASID */ 346*d7d5b05fSDeng-Cheng Zhu write_c0_entryhi(old_entryhi); 347*d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 348*d7d5b05fSDeng-Cheng Zhu tlbw_use_hazard(); 349*d7d5b05fSDeng-Cheng Zhu local_irq_restore(flags); 350*d7d5b05fSDeng-Cheng Zhu 351*d7d5b05fSDeng-Cheng Zhu return 0; 352*d7d5b05fSDeng-Cheng Zhu } 353*d7d5b05fSDeng-Cheng Zhu EXPORT_SYMBOL(kvm_mips_handle_commpage_tlb_fault); 354*d7d5b05fSDeng-Cheng Zhu 355*d7d5b05fSDeng-Cheng Zhu int kvm_mips_handle_mapped_seg_tlb_fault(struct kvm_vcpu *vcpu, 356*d7d5b05fSDeng-Cheng Zhu struct kvm_mips_tlb *tlb, 357*d7d5b05fSDeng-Cheng Zhu unsigned long *hpa0, 358*d7d5b05fSDeng-Cheng Zhu unsigned long *hpa1) 359*d7d5b05fSDeng-Cheng Zhu { 360*d7d5b05fSDeng-Cheng Zhu unsigned long entryhi = 0, entrylo0 = 0, entrylo1 = 0; 361*d7d5b05fSDeng-Cheng Zhu struct kvm *kvm = vcpu->kvm; 362*d7d5b05fSDeng-Cheng Zhu pfn_t pfn0, pfn1; 363*d7d5b05fSDeng-Cheng Zhu 364*d7d5b05fSDeng-Cheng Zhu if ((tlb->tlb_hi & VPN2_MASK) == 0) { 365*d7d5b05fSDeng-Cheng Zhu pfn0 = 0; 366*d7d5b05fSDeng-Cheng Zhu pfn1 = 0; 367*d7d5b05fSDeng-Cheng Zhu } else { 368*d7d5b05fSDeng-Cheng Zhu if (kvm_mips_map_page(kvm, mips3_tlbpfn_to_paddr(tlb->tlb_lo0) 369*d7d5b05fSDeng-Cheng Zhu >> PAGE_SHIFT) < 0) 370*d7d5b05fSDeng-Cheng Zhu return -1; 371*d7d5b05fSDeng-Cheng Zhu 372*d7d5b05fSDeng-Cheng Zhu if (kvm_mips_map_page(kvm, mips3_tlbpfn_to_paddr(tlb->tlb_lo1) 373*d7d5b05fSDeng-Cheng Zhu >> PAGE_SHIFT) < 0) 374*d7d5b05fSDeng-Cheng Zhu return -1; 375*d7d5b05fSDeng-Cheng Zhu 376*d7d5b05fSDeng-Cheng Zhu pfn0 = kvm->arch.guest_pmap[mips3_tlbpfn_to_paddr(tlb->tlb_lo0) 377*d7d5b05fSDeng-Cheng Zhu >> PAGE_SHIFT]; 378*d7d5b05fSDeng-Cheng Zhu pfn1 = kvm->arch.guest_pmap[mips3_tlbpfn_to_paddr(tlb->tlb_lo1) 379*d7d5b05fSDeng-Cheng Zhu >> PAGE_SHIFT]; 380*d7d5b05fSDeng-Cheng Zhu } 381*d7d5b05fSDeng-Cheng Zhu 382*d7d5b05fSDeng-Cheng Zhu if (hpa0) 383*d7d5b05fSDeng-Cheng Zhu *hpa0 = pfn0 << PAGE_SHIFT; 384*d7d5b05fSDeng-Cheng Zhu 385*d7d5b05fSDeng-Cheng Zhu if (hpa1) 386*d7d5b05fSDeng-Cheng Zhu *hpa1 = pfn1 << PAGE_SHIFT; 387*d7d5b05fSDeng-Cheng Zhu 388*d7d5b05fSDeng-Cheng Zhu /* Get attributes from the Guest TLB */ 389*d7d5b05fSDeng-Cheng Zhu entryhi = (tlb->tlb_hi & VPN2_MASK) | (KVM_GUEST_KERNEL_MODE(vcpu) ? 390*d7d5b05fSDeng-Cheng Zhu kvm_mips_get_kernel_asid(vcpu) : 391*d7d5b05fSDeng-Cheng Zhu kvm_mips_get_user_asid(vcpu)); 392*d7d5b05fSDeng-Cheng Zhu entrylo0 = mips3_paddr_to_tlbpfn(pfn0 << PAGE_SHIFT) | (0x3 << 3) | 393*d7d5b05fSDeng-Cheng Zhu (tlb->tlb_lo0 & MIPS3_PG_D) | (tlb->tlb_lo0 & MIPS3_PG_V); 394*d7d5b05fSDeng-Cheng Zhu entrylo1 = mips3_paddr_to_tlbpfn(pfn1 << PAGE_SHIFT) | (0x3 << 3) | 395*d7d5b05fSDeng-Cheng Zhu (tlb->tlb_lo1 & MIPS3_PG_D) | (tlb->tlb_lo1 & MIPS3_PG_V); 396*d7d5b05fSDeng-Cheng Zhu 397*d7d5b05fSDeng-Cheng Zhu kvm_debug("@ %#lx tlb_lo0: 0x%08lx tlb_lo1: 0x%08lx\n", vcpu->arch.pc, 398*d7d5b05fSDeng-Cheng Zhu tlb->tlb_lo0, tlb->tlb_lo1); 399*d7d5b05fSDeng-Cheng Zhu 400*d7d5b05fSDeng-Cheng Zhu return kvm_mips_host_tlb_write(vcpu, entryhi, entrylo0, entrylo1, 401*d7d5b05fSDeng-Cheng Zhu tlb->tlb_mask); 402*d7d5b05fSDeng-Cheng Zhu } 403*d7d5b05fSDeng-Cheng Zhu EXPORT_SYMBOL(kvm_mips_handle_mapped_seg_tlb_fault); 404*d7d5b05fSDeng-Cheng Zhu 405*d7d5b05fSDeng-Cheng Zhu int kvm_mips_guest_tlb_lookup(struct kvm_vcpu *vcpu, unsigned long entryhi) 406*d7d5b05fSDeng-Cheng Zhu { 407*d7d5b05fSDeng-Cheng Zhu int i; 408*d7d5b05fSDeng-Cheng Zhu int index = -1; 409*d7d5b05fSDeng-Cheng Zhu struct kvm_mips_tlb *tlb = vcpu->arch.guest_tlb; 410*d7d5b05fSDeng-Cheng Zhu 411*d7d5b05fSDeng-Cheng Zhu for (i = 0; i < KVM_MIPS_GUEST_TLB_SIZE; i++) { 412*d7d5b05fSDeng-Cheng Zhu if (TLB_HI_VPN2_HIT(tlb[i], entryhi) && 413*d7d5b05fSDeng-Cheng Zhu TLB_HI_ASID_HIT(tlb[i], entryhi)) { 414*d7d5b05fSDeng-Cheng Zhu index = i; 415*d7d5b05fSDeng-Cheng Zhu break; 416*d7d5b05fSDeng-Cheng Zhu } 417*d7d5b05fSDeng-Cheng Zhu } 418*d7d5b05fSDeng-Cheng Zhu 419*d7d5b05fSDeng-Cheng Zhu kvm_debug("%s: entryhi: %#lx, index: %d lo0: %#lx, lo1: %#lx\n", 420*d7d5b05fSDeng-Cheng Zhu __func__, entryhi, index, tlb[i].tlb_lo0, tlb[i].tlb_lo1); 421*d7d5b05fSDeng-Cheng Zhu 422*d7d5b05fSDeng-Cheng Zhu return index; 423*d7d5b05fSDeng-Cheng Zhu } 424*d7d5b05fSDeng-Cheng Zhu EXPORT_SYMBOL(kvm_mips_guest_tlb_lookup); 425*d7d5b05fSDeng-Cheng Zhu 426*d7d5b05fSDeng-Cheng Zhu int kvm_mips_host_tlb_lookup(struct kvm_vcpu *vcpu, unsigned long vaddr) 427*d7d5b05fSDeng-Cheng Zhu { 428*d7d5b05fSDeng-Cheng Zhu unsigned long old_entryhi, flags; 429*d7d5b05fSDeng-Cheng Zhu int idx; 430*d7d5b05fSDeng-Cheng Zhu 431*d7d5b05fSDeng-Cheng Zhu local_irq_save(flags); 432*d7d5b05fSDeng-Cheng Zhu 433*d7d5b05fSDeng-Cheng Zhu old_entryhi = read_c0_entryhi(); 434*d7d5b05fSDeng-Cheng Zhu 435*d7d5b05fSDeng-Cheng Zhu if (KVM_GUEST_KERNEL_MODE(vcpu)) 436*d7d5b05fSDeng-Cheng Zhu write_c0_entryhi((vaddr & VPN2_MASK) | 437*d7d5b05fSDeng-Cheng Zhu kvm_mips_get_kernel_asid(vcpu)); 438*d7d5b05fSDeng-Cheng Zhu else { 439*d7d5b05fSDeng-Cheng Zhu write_c0_entryhi((vaddr & VPN2_MASK) | 440*d7d5b05fSDeng-Cheng Zhu kvm_mips_get_user_asid(vcpu)); 441*d7d5b05fSDeng-Cheng Zhu } 442*d7d5b05fSDeng-Cheng Zhu 443*d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 444*d7d5b05fSDeng-Cheng Zhu 445*d7d5b05fSDeng-Cheng Zhu tlb_probe(); 446*d7d5b05fSDeng-Cheng Zhu tlb_probe_hazard(); 447*d7d5b05fSDeng-Cheng Zhu idx = read_c0_index(); 448*d7d5b05fSDeng-Cheng Zhu 449*d7d5b05fSDeng-Cheng Zhu /* Restore old ASID */ 450*d7d5b05fSDeng-Cheng Zhu write_c0_entryhi(old_entryhi); 451*d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 452*d7d5b05fSDeng-Cheng Zhu tlbw_use_hazard(); 453*d7d5b05fSDeng-Cheng Zhu 454*d7d5b05fSDeng-Cheng Zhu local_irq_restore(flags); 455*d7d5b05fSDeng-Cheng Zhu 456*d7d5b05fSDeng-Cheng Zhu kvm_debug("Host TLB lookup, %#lx, idx: %2d\n", vaddr, idx); 457*d7d5b05fSDeng-Cheng Zhu 458*d7d5b05fSDeng-Cheng Zhu return idx; 459*d7d5b05fSDeng-Cheng Zhu } 460*d7d5b05fSDeng-Cheng Zhu EXPORT_SYMBOL(kvm_mips_host_tlb_lookup); 461*d7d5b05fSDeng-Cheng Zhu 462*d7d5b05fSDeng-Cheng Zhu int kvm_mips_host_tlb_inv(struct kvm_vcpu *vcpu, unsigned long va) 463*d7d5b05fSDeng-Cheng Zhu { 464*d7d5b05fSDeng-Cheng Zhu int idx; 465*d7d5b05fSDeng-Cheng Zhu unsigned long flags, old_entryhi; 466*d7d5b05fSDeng-Cheng Zhu 467*d7d5b05fSDeng-Cheng Zhu local_irq_save(flags); 468*d7d5b05fSDeng-Cheng Zhu 469*d7d5b05fSDeng-Cheng Zhu old_entryhi = read_c0_entryhi(); 470*d7d5b05fSDeng-Cheng Zhu 471*d7d5b05fSDeng-Cheng Zhu write_c0_entryhi((va & VPN2_MASK) | kvm_mips_get_user_asid(vcpu)); 472*d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 473*d7d5b05fSDeng-Cheng Zhu 474*d7d5b05fSDeng-Cheng Zhu tlb_probe(); 475*d7d5b05fSDeng-Cheng Zhu tlb_probe_hazard(); 476*d7d5b05fSDeng-Cheng Zhu idx = read_c0_index(); 477*d7d5b05fSDeng-Cheng Zhu 478*d7d5b05fSDeng-Cheng Zhu if (idx >= current_cpu_data.tlbsize) 479*d7d5b05fSDeng-Cheng Zhu BUG(); 480*d7d5b05fSDeng-Cheng Zhu 481*d7d5b05fSDeng-Cheng Zhu if (idx > 0) { 482*d7d5b05fSDeng-Cheng Zhu write_c0_entryhi(UNIQUE_ENTRYHI(idx)); 483*d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 484*d7d5b05fSDeng-Cheng Zhu 485*d7d5b05fSDeng-Cheng Zhu write_c0_entrylo0(0); 486*d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 487*d7d5b05fSDeng-Cheng Zhu 488*d7d5b05fSDeng-Cheng Zhu write_c0_entrylo1(0); 489*d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 490*d7d5b05fSDeng-Cheng Zhu 491*d7d5b05fSDeng-Cheng Zhu tlb_write_indexed(); 492*d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 493*d7d5b05fSDeng-Cheng Zhu } 494*d7d5b05fSDeng-Cheng Zhu 495*d7d5b05fSDeng-Cheng Zhu write_c0_entryhi(old_entryhi); 496*d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 497*d7d5b05fSDeng-Cheng Zhu tlbw_use_hazard(); 498*d7d5b05fSDeng-Cheng Zhu 499*d7d5b05fSDeng-Cheng Zhu local_irq_restore(flags); 500*d7d5b05fSDeng-Cheng Zhu 501*d7d5b05fSDeng-Cheng Zhu if (idx > 0) 502*d7d5b05fSDeng-Cheng Zhu kvm_debug("%s: Invalidated entryhi %#lx @ idx %d\n", __func__, 503*d7d5b05fSDeng-Cheng Zhu (va & VPN2_MASK) | kvm_mips_get_user_asid(vcpu), idx); 504*d7d5b05fSDeng-Cheng Zhu 505*d7d5b05fSDeng-Cheng Zhu return 0; 506*d7d5b05fSDeng-Cheng Zhu } 507*d7d5b05fSDeng-Cheng Zhu EXPORT_SYMBOL(kvm_mips_host_tlb_inv); 508*d7d5b05fSDeng-Cheng Zhu 509*d7d5b05fSDeng-Cheng Zhu /* XXXKYMA: Fix Guest USER/KERNEL no longer share the same ASID */ 510*d7d5b05fSDeng-Cheng Zhu int kvm_mips_host_tlb_inv_index(struct kvm_vcpu *vcpu, int index) 511*d7d5b05fSDeng-Cheng Zhu { 512*d7d5b05fSDeng-Cheng Zhu unsigned long flags, old_entryhi; 513*d7d5b05fSDeng-Cheng Zhu 514*d7d5b05fSDeng-Cheng Zhu if (index >= current_cpu_data.tlbsize) 515*d7d5b05fSDeng-Cheng Zhu BUG(); 516*d7d5b05fSDeng-Cheng Zhu 517*d7d5b05fSDeng-Cheng Zhu local_irq_save(flags); 518*d7d5b05fSDeng-Cheng Zhu 519*d7d5b05fSDeng-Cheng Zhu old_entryhi = read_c0_entryhi(); 520*d7d5b05fSDeng-Cheng Zhu 521*d7d5b05fSDeng-Cheng Zhu write_c0_entryhi(UNIQUE_ENTRYHI(index)); 522*d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 523*d7d5b05fSDeng-Cheng Zhu 524*d7d5b05fSDeng-Cheng Zhu write_c0_index(index); 525*d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 526*d7d5b05fSDeng-Cheng Zhu 527*d7d5b05fSDeng-Cheng Zhu write_c0_entrylo0(0); 528*d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 529*d7d5b05fSDeng-Cheng Zhu 530*d7d5b05fSDeng-Cheng Zhu write_c0_entrylo1(0); 531*d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 532*d7d5b05fSDeng-Cheng Zhu 533*d7d5b05fSDeng-Cheng Zhu tlb_write_indexed(); 534*d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 535*d7d5b05fSDeng-Cheng Zhu tlbw_use_hazard(); 536*d7d5b05fSDeng-Cheng Zhu 537*d7d5b05fSDeng-Cheng Zhu write_c0_entryhi(old_entryhi); 538*d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 539*d7d5b05fSDeng-Cheng Zhu tlbw_use_hazard(); 540*d7d5b05fSDeng-Cheng Zhu 541*d7d5b05fSDeng-Cheng Zhu local_irq_restore(flags); 542*d7d5b05fSDeng-Cheng Zhu 543*d7d5b05fSDeng-Cheng Zhu return 0; 544*d7d5b05fSDeng-Cheng Zhu } 545*d7d5b05fSDeng-Cheng Zhu 546*d7d5b05fSDeng-Cheng Zhu void kvm_mips_flush_host_tlb(int skip_kseg0) 547*d7d5b05fSDeng-Cheng Zhu { 548*d7d5b05fSDeng-Cheng Zhu unsigned long flags; 549*d7d5b05fSDeng-Cheng Zhu unsigned long old_entryhi, entryhi; 550*d7d5b05fSDeng-Cheng Zhu unsigned long old_pagemask; 551*d7d5b05fSDeng-Cheng Zhu int entry = 0; 552*d7d5b05fSDeng-Cheng Zhu int maxentry = current_cpu_data.tlbsize; 553*d7d5b05fSDeng-Cheng Zhu 554*d7d5b05fSDeng-Cheng Zhu local_irq_save(flags); 555*d7d5b05fSDeng-Cheng Zhu 556*d7d5b05fSDeng-Cheng Zhu old_entryhi = read_c0_entryhi(); 557*d7d5b05fSDeng-Cheng Zhu old_pagemask = read_c0_pagemask(); 558*d7d5b05fSDeng-Cheng Zhu 559*d7d5b05fSDeng-Cheng Zhu /* Blast 'em all away. */ 560*d7d5b05fSDeng-Cheng Zhu for (entry = 0; entry < maxentry; entry++) { 561*d7d5b05fSDeng-Cheng Zhu write_c0_index(entry); 562*d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 563*d7d5b05fSDeng-Cheng Zhu 564*d7d5b05fSDeng-Cheng Zhu if (skip_kseg0) { 565*d7d5b05fSDeng-Cheng Zhu tlb_read(); 566*d7d5b05fSDeng-Cheng Zhu tlbw_use_hazard(); 567*d7d5b05fSDeng-Cheng Zhu 568*d7d5b05fSDeng-Cheng Zhu entryhi = read_c0_entryhi(); 569*d7d5b05fSDeng-Cheng Zhu 570*d7d5b05fSDeng-Cheng Zhu /* Don't blow away guest kernel entries */ 571*d7d5b05fSDeng-Cheng Zhu if (KVM_GUEST_KSEGX(entryhi) == KVM_GUEST_KSEG0) 572*d7d5b05fSDeng-Cheng Zhu continue; 573*d7d5b05fSDeng-Cheng Zhu } 574*d7d5b05fSDeng-Cheng Zhu 575*d7d5b05fSDeng-Cheng Zhu /* Make sure all entries differ. */ 576*d7d5b05fSDeng-Cheng Zhu write_c0_entryhi(UNIQUE_ENTRYHI(entry)); 577*d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 578*d7d5b05fSDeng-Cheng Zhu write_c0_entrylo0(0); 579*d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 580*d7d5b05fSDeng-Cheng Zhu write_c0_entrylo1(0); 581*d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 582*d7d5b05fSDeng-Cheng Zhu 583*d7d5b05fSDeng-Cheng Zhu tlb_write_indexed(); 584*d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 585*d7d5b05fSDeng-Cheng Zhu } 586*d7d5b05fSDeng-Cheng Zhu 587*d7d5b05fSDeng-Cheng Zhu tlbw_use_hazard(); 588*d7d5b05fSDeng-Cheng Zhu 589*d7d5b05fSDeng-Cheng Zhu write_c0_entryhi(old_entryhi); 590*d7d5b05fSDeng-Cheng Zhu write_c0_pagemask(old_pagemask); 591*d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 592*d7d5b05fSDeng-Cheng Zhu tlbw_use_hazard(); 593*d7d5b05fSDeng-Cheng Zhu 594*d7d5b05fSDeng-Cheng Zhu local_irq_restore(flags); 595*d7d5b05fSDeng-Cheng Zhu } 596*d7d5b05fSDeng-Cheng Zhu EXPORT_SYMBOL(kvm_mips_flush_host_tlb); 597*d7d5b05fSDeng-Cheng Zhu 598*d7d5b05fSDeng-Cheng Zhu void kvm_get_new_mmu_context(struct mm_struct *mm, unsigned long cpu, 599*d7d5b05fSDeng-Cheng Zhu struct kvm_vcpu *vcpu) 600*d7d5b05fSDeng-Cheng Zhu { 601*d7d5b05fSDeng-Cheng Zhu unsigned long asid = asid_cache(cpu); 602*d7d5b05fSDeng-Cheng Zhu 603*d7d5b05fSDeng-Cheng Zhu asid += ASID_INC; 604*d7d5b05fSDeng-Cheng Zhu if (!(asid & ASID_MASK)) { 605*d7d5b05fSDeng-Cheng Zhu if (cpu_has_vtag_icache) 606*d7d5b05fSDeng-Cheng Zhu flush_icache_all(); 607*d7d5b05fSDeng-Cheng Zhu 608*d7d5b05fSDeng-Cheng Zhu kvm_local_flush_tlb_all(); /* start new asid cycle */ 609*d7d5b05fSDeng-Cheng Zhu 610*d7d5b05fSDeng-Cheng Zhu if (!asid) /* fix version if needed */ 611*d7d5b05fSDeng-Cheng Zhu asid = ASID_FIRST_VERSION; 612*d7d5b05fSDeng-Cheng Zhu } 613*d7d5b05fSDeng-Cheng Zhu 614*d7d5b05fSDeng-Cheng Zhu cpu_context(cpu, mm) = asid_cache(cpu) = asid; 615*d7d5b05fSDeng-Cheng Zhu } 616*d7d5b05fSDeng-Cheng Zhu 617*d7d5b05fSDeng-Cheng Zhu void kvm_local_flush_tlb_all(void) 618*d7d5b05fSDeng-Cheng Zhu { 619*d7d5b05fSDeng-Cheng Zhu unsigned long flags; 620*d7d5b05fSDeng-Cheng Zhu unsigned long old_ctx; 621*d7d5b05fSDeng-Cheng Zhu int entry = 0; 622*d7d5b05fSDeng-Cheng Zhu 623*d7d5b05fSDeng-Cheng Zhu local_irq_save(flags); 624*d7d5b05fSDeng-Cheng Zhu /* Save old context and create impossible VPN2 value */ 625*d7d5b05fSDeng-Cheng Zhu old_ctx = read_c0_entryhi(); 626*d7d5b05fSDeng-Cheng Zhu write_c0_entrylo0(0); 627*d7d5b05fSDeng-Cheng Zhu write_c0_entrylo1(0); 628*d7d5b05fSDeng-Cheng Zhu 629*d7d5b05fSDeng-Cheng Zhu /* Blast 'em all away. */ 630*d7d5b05fSDeng-Cheng Zhu while (entry < current_cpu_data.tlbsize) { 631*d7d5b05fSDeng-Cheng Zhu /* Make sure all entries differ. */ 632*d7d5b05fSDeng-Cheng Zhu write_c0_entryhi(UNIQUE_ENTRYHI(entry)); 633*d7d5b05fSDeng-Cheng Zhu write_c0_index(entry); 634*d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 635*d7d5b05fSDeng-Cheng Zhu tlb_write_indexed(); 636*d7d5b05fSDeng-Cheng Zhu entry++; 637*d7d5b05fSDeng-Cheng Zhu } 638*d7d5b05fSDeng-Cheng Zhu tlbw_use_hazard(); 639*d7d5b05fSDeng-Cheng Zhu write_c0_entryhi(old_ctx); 640*d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 641*d7d5b05fSDeng-Cheng Zhu 642*d7d5b05fSDeng-Cheng Zhu local_irq_restore(flags); 643*d7d5b05fSDeng-Cheng Zhu } 644*d7d5b05fSDeng-Cheng Zhu EXPORT_SYMBOL(kvm_local_flush_tlb_all); 645*d7d5b05fSDeng-Cheng Zhu 646*d7d5b05fSDeng-Cheng Zhu /** 647*d7d5b05fSDeng-Cheng Zhu * kvm_mips_migrate_count() - Migrate timer. 648*d7d5b05fSDeng-Cheng Zhu * @vcpu: Virtual CPU. 649*d7d5b05fSDeng-Cheng Zhu * 650*d7d5b05fSDeng-Cheng Zhu * Migrate CP0_Count hrtimer to the current CPU by cancelling and restarting it 651*d7d5b05fSDeng-Cheng Zhu * if it was running prior to being cancelled. 652*d7d5b05fSDeng-Cheng Zhu * 653*d7d5b05fSDeng-Cheng Zhu * Must be called when the VCPU is migrated to a different CPU to ensure that 654*d7d5b05fSDeng-Cheng Zhu * timer expiry during guest execution interrupts the guest and causes the 655*d7d5b05fSDeng-Cheng Zhu * interrupt to be delivered in a timely manner. 656*d7d5b05fSDeng-Cheng Zhu */ 657*d7d5b05fSDeng-Cheng Zhu static void kvm_mips_migrate_count(struct kvm_vcpu *vcpu) 658*d7d5b05fSDeng-Cheng Zhu { 659*d7d5b05fSDeng-Cheng Zhu if (hrtimer_cancel(&vcpu->arch.comparecount_timer)) 660*d7d5b05fSDeng-Cheng Zhu hrtimer_restart(&vcpu->arch.comparecount_timer); 661*d7d5b05fSDeng-Cheng Zhu } 662*d7d5b05fSDeng-Cheng Zhu 663*d7d5b05fSDeng-Cheng Zhu /* Restore ASID once we are scheduled back after preemption */ 664*d7d5b05fSDeng-Cheng Zhu void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu) 665*d7d5b05fSDeng-Cheng Zhu { 666*d7d5b05fSDeng-Cheng Zhu unsigned long flags; 667*d7d5b05fSDeng-Cheng Zhu int newasid = 0; 668*d7d5b05fSDeng-Cheng Zhu 669*d7d5b05fSDeng-Cheng Zhu kvm_debug("%s: vcpu %p, cpu: %d\n", __func__, vcpu, cpu); 670*d7d5b05fSDeng-Cheng Zhu 671*d7d5b05fSDeng-Cheng Zhu /* Alocate new kernel and user ASIDs if needed */ 672*d7d5b05fSDeng-Cheng Zhu 673*d7d5b05fSDeng-Cheng Zhu local_irq_save(flags); 674*d7d5b05fSDeng-Cheng Zhu 675*d7d5b05fSDeng-Cheng Zhu if (((vcpu->arch. 676*d7d5b05fSDeng-Cheng Zhu guest_kernel_asid[cpu] ^ asid_cache(cpu)) & ASID_VERSION_MASK)) { 677*d7d5b05fSDeng-Cheng Zhu kvm_get_new_mmu_context(&vcpu->arch.guest_kernel_mm, cpu, vcpu); 678*d7d5b05fSDeng-Cheng Zhu vcpu->arch.guest_kernel_asid[cpu] = 679*d7d5b05fSDeng-Cheng Zhu vcpu->arch.guest_kernel_mm.context.asid[cpu]; 680*d7d5b05fSDeng-Cheng Zhu kvm_get_new_mmu_context(&vcpu->arch.guest_user_mm, cpu, vcpu); 681*d7d5b05fSDeng-Cheng Zhu vcpu->arch.guest_user_asid[cpu] = 682*d7d5b05fSDeng-Cheng Zhu vcpu->arch.guest_user_mm.context.asid[cpu]; 683*d7d5b05fSDeng-Cheng Zhu newasid++; 684*d7d5b05fSDeng-Cheng Zhu 685*d7d5b05fSDeng-Cheng Zhu kvm_debug("[%d]: cpu_context: %#lx\n", cpu, 686*d7d5b05fSDeng-Cheng Zhu cpu_context(cpu, current->mm)); 687*d7d5b05fSDeng-Cheng Zhu kvm_debug("[%d]: Allocated new ASID for Guest Kernel: %#x\n", 688*d7d5b05fSDeng-Cheng Zhu cpu, vcpu->arch.guest_kernel_asid[cpu]); 689*d7d5b05fSDeng-Cheng Zhu kvm_debug("[%d]: Allocated new ASID for Guest User: %#x\n", cpu, 690*d7d5b05fSDeng-Cheng Zhu vcpu->arch.guest_user_asid[cpu]); 691*d7d5b05fSDeng-Cheng Zhu } 692*d7d5b05fSDeng-Cheng Zhu 693*d7d5b05fSDeng-Cheng Zhu if (vcpu->arch.last_sched_cpu != cpu) { 694*d7d5b05fSDeng-Cheng Zhu kvm_debug("[%d->%d]KVM VCPU[%d] switch\n", 695*d7d5b05fSDeng-Cheng Zhu vcpu->arch.last_sched_cpu, cpu, vcpu->vcpu_id); 696*d7d5b05fSDeng-Cheng Zhu /* 697*d7d5b05fSDeng-Cheng Zhu * Migrate the timer interrupt to the current CPU so that it 698*d7d5b05fSDeng-Cheng Zhu * always interrupts the guest and synchronously triggers a 699*d7d5b05fSDeng-Cheng Zhu * guest timer interrupt. 700*d7d5b05fSDeng-Cheng Zhu */ 701*d7d5b05fSDeng-Cheng Zhu kvm_mips_migrate_count(vcpu); 702*d7d5b05fSDeng-Cheng Zhu } 703*d7d5b05fSDeng-Cheng Zhu 704*d7d5b05fSDeng-Cheng Zhu if (!newasid) { 705*d7d5b05fSDeng-Cheng Zhu /* 706*d7d5b05fSDeng-Cheng Zhu * If we preempted while the guest was executing, then reload 707*d7d5b05fSDeng-Cheng Zhu * the pre-empted ASID 708*d7d5b05fSDeng-Cheng Zhu */ 709*d7d5b05fSDeng-Cheng Zhu if (current->flags & PF_VCPU) { 710*d7d5b05fSDeng-Cheng Zhu write_c0_entryhi(vcpu->arch. 711*d7d5b05fSDeng-Cheng Zhu preempt_entryhi & ASID_MASK); 712*d7d5b05fSDeng-Cheng Zhu ehb(); 713*d7d5b05fSDeng-Cheng Zhu } 714*d7d5b05fSDeng-Cheng Zhu } else { 715*d7d5b05fSDeng-Cheng Zhu /* New ASIDs were allocated for the VM */ 716*d7d5b05fSDeng-Cheng Zhu 717*d7d5b05fSDeng-Cheng Zhu /* 718*d7d5b05fSDeng-Cheng Zhu * Were we in guest context? If so then the pre-empted ASID is 719*d7d5b05fSDeng-Cheng Zhu * no longer valid, we need to set it to what it should be based 720*d7d5b05fSDeng-Cheng Zhu * on the mode of the Guest (Kernel/User) 721*d7d5b05fSDeng-Cheng Zhu */ 722*d7d5b05fSDeng-Cheng Zhu if (current->flags & PF_VCPU) { 723*d7d5b05fSDeng-Cheng Zhu if (KVM_GUEST_KERNEL_MODE(vcpu)) 724*d7d5b05fSDeng-Cheng Zhu write_c0_entryhi(vcpu->arch. 725*d7d5b05fSDeng-Cheng Zhu guest_kernel_asid[cpu] & 726*d7d5b05fSDeng-Cheng Zhu ASID_MASK); 727*d7d5b05fSDeng-Cheng Zhu else 728*d7d5b05fSDeng-Cheng Zhu write_c0_entryhi(vcpu->arch. 729*d7d5b05fSDeng-Cheng Zhu guest_user_asid[cpu] & 730*d7d5b05fSDeng-Cheng Zhu ASID_MASK); 731*d7d5b05fSDeng-Cheng Zhu ehb(); 732*d7d5b05fSDeng-Cheng Zhu } 733*d7d5b05fSDeng-Cheng Zhu } 734*d7d5b05fSDeng-Cheng Zhu 735*d7d5b05fSDeng-Cheng Zhu local_irq_restore(flags); 736*d7d5b05fSDeng-Cheng Zhu 737*d7d5b05fSDeng-Cheng Zhu } 738*d7d5b05fSDeng-Cheng Zhu EXPORT_SYMBOL(kvm_arch_vcpu_load); 739*d7d5b05fSDeng-Cheng Zhu 740*d7d5b05fSDeng-Cheng Zhu /* ASID can change if another task is scheduled during preemption */ 741*d7d5b05fSDeng-Cheng Zhu void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu) 742*d7d5b05fSDeng-Cheng Zhu { 743*d7d5b05fSDeng-Cheng Zhu unsigned long flags; 744*d7d5b05fSDeng-Cheng Zhu uint32_t cpu; 745*d7d5b05fSDeng-Cheng Zhu 746*d7d5b05fSDeng-Cheng Zhu local_irq_save(flags); 747*d7d5b05fSDeng-Cheng Zhu 748*d7d5b05fSDeng-Cheng Zhu cpu = smp_processor_id(); 749*d7d5b05fSDeng-Cheng Zhu 750*d7d5b05fSDeng-Cheng Zhu vcpu->arch.preempt_entryhi = read_c0_entryhi(); 751*d7d5b05fSDeng-Cheng Zhu vcpu->arch.last_sched_cpu = cpu; 752*d7d5b05fSDeng-Cheng Zhu 753*d7d5b05fSDeng-Cheng Zhu if (((cpu_context(cpu, current->mm) ^ asid_cache(cpu)) & 754*d7d5b05fSDeng-Cheng Zhu ASID_VERSION_MASK)) { 755*d7d5b05fSDeng-Cheng Zhu kvm_debug("%s: Dropping MMU Context: %#lx\n", __func__, 756*d7d5b05fSDeng-Cheng Zhu cpu_context(cpu, current->mm)); 757*d7d5b05fSDeng-Cheng Zhu drop_mmu_context(current->mm, cpu); 758*d7d5b05fSDeng-Cheng Zhu } 759*d7d5b05fSDeng-Cheng Zhu write_c0_entryhi(cpu_asid(cpu, current->mm)); 760*d7d5b05fSDeng-Cheng Zhu ehb(); 761*d7d5b05fSDeng-Cheng Zhu 762*d7d5b05fSDeng-Cheng Zhu local_irq_restore(flags); 763*d7d5b05fSDeng-Cheng Zhu } 764*d7d5b05fSDeng-Cheng Zhu EXPORT_SYMBOL(kvm_arch_vcpu_put); 765*d7d5b05fSDeng-Cheng Zhu 766*d7d5b05fSDeng-Cheng Zhu uint32_t kvm_get_inst(uint32_t *opc, struct kvm_vcpu *vcpu) 767*d7d5b05fSDeng-Cheng Zhu { 768*d7d5b05fSDeng-Cheng Zhu struct mips_coproc *cop0 = vcpu->arch.cop0; 769*d7d5b05fSDeng-Cheng Zhu unsigned long paddr, flags, vpn2, asid; 770*d7d5b05fSDeng-Cheng Zhu uint32_t inst; 771*d7d5b05fSDeng-Cheng Zhu int index; 772*d7d5b05fSDeng-Cheng Zhu 773*d7d5b05fSDeng-Cheng Zhu if (KVM_GUEST_KSEGX((unsigned long) opc) < KVM_GUEST_KSEG0 || 774*d7d5b05fSDeng-Cheng Zhu KVM_GUEST_KSEGX((unsigned long) opc) == KVM_GUEST_KSEG23) { 775*d7d5b05fSDeng-Cheng Zhu local_irq_save(flags); 776*d7d5b05fSDeng-Cheng Zhu index = kvm_mips_host_tlb_lookup(vcpu, (unsigned long) opc); 777*d7d5b05fSDeng-Cheng Zhu if (index >= 0) { 778*d7d5b05fSDeng-Cheng Zhu inst = *(opc); 779*d7d5b05fSDeng-Cheng Zhu } else { 780*d7d5b05fSDeng-Cheng Zhu vpn2 = (unsigned long) opc & VPN2_MASK; 781*d7d5b05fSDeng-Cheng Zhu asid = kvm_read_c0_guest_entryhi(cop0) & ASID_MASK; 782*d7d5b05fSDeng-Cheng Zhu index = kvm_mips_guest_tlb_lookup(vcpu, vpn2 | asid); 783*d7d5b05fSDeng-Cheng Zhu if (index < 0) { 784*d7d5b05fSDeng-Cheng Zhu kvm_err("%s: get_user_failed for %p, vcpu: %p, ASID: %#lx\n", 785*d7d5b05fSDeng-Cheng Zhu __func__, opc, vcpu, read_c0_entryhi()); 786*d7d5b05fSDeng-Cheng Zhu kvm_mips_dump_host_tlbs(); 787*d7d5b05fSDeng-Cheng Zhu local_irq_restore(flags); 788*d7d5b05fSDeng-Cheng Zhu return KVM_INVALID_INST; 789*d7d5b05fSDeng-Cheng Zhu } 790*d7d5b05fSDeng-Cheng Zhu kvm_mips_handle_mapped_seg_tlb_fault(vcpu, 791*d7d5b05fSDeng-Cheng Zhu &vcpu->arch. 792*d7d5b05fSDeng-Cheng Zhu guest_tlb[index], 793*d7d5b05fSDeng-Cheng Zhu NULL, NULL); 794*d7d5b05fSDeng-Cheng Zhu inst = *(opc); 795*d7d5b05fSDeng-Cheng Zhu } 796*d7d5b05fSDeng-Cheng Zhu local_irq_restore(flags); 797*d7d5b05fSDeng-Cheng Zhu } else if (KVM_GUEST_KSEGX(opc) == KVM_GUEST_KSEG0) { 798*d7d5b05fSDeng-Cheng Zhu paddr = 799*d7d5b05fSDeng-Cheng Zhu kvm_mips_translate_guest_kseg0_to_hpa(vcpu, 800*d7d5b05fSDeng-Cheng Zhu (unsigned long) opc); 801*d7d5b05fSDeng-Cheng Zhu inst = *(uint32_t *) CKSEG0ADDR(paddr); 802*d7d5b05fSDeng-Cheng Zhu } else { 803*d7d5b05fSDeng-Cheng Zhu kvm_err("%s: illegal address: %p\n", __func__, opc); 804*d7d5b05fSDeng-Cheng Zhu return KVM_INVALID_INST; 805*d7d5b05fSDeng-Cheng Zhu } 806*d7d5b05fSDeng-Cheng Zhu 807*d7d5b05fSDeng-Cheng Zhu return inst; 808*d7d5b05fSDeng-Cheng Zhu } 809*d7d5b05fSDeng-Cheng Zhu EXPORT_SYMBOL(kvm_get_inst); 810