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/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 33c992a4f6SJames Hogan unsigned long GUESTID_MASK; 34c992a4f6SJames Hogan EXPORT_SYMBOL_GPL(GUESTID_MASK); 35c992a4f6SJames Hogan unsigned long GUESTID_FIRST_VERSION; 36c992a4f6SJames Hogan EXPORT_SYMBOL_GPL(GUESTID_FIRST_VERSION); 37c992a4f6SJames Hogan unsigned long GUESTID_VERSION_MASK; 38c992a4f6SJames Hogan EXPORT_SYMBOL_GPL(GUESTID_VERSION_MASK); 39c992a4f6SJames Hogan 40372582a6SJames Hogan static u32 kvm_mips_get_root_asid(struct kvm_vcpu *vcpu) 41372582a6SJames Hogan { 42372582a6SJames Hogan struct mm_struct *gpa_mm = &vcpu->kvm->arch.gpa_mm; 43372582a6SJames Hogan 44372582a6SJames Hogan if (cpu_has_guestid) 45372582a6SJames Hogan return 0; 46372582a6SJames Hogan else 47372582a6SJames Hogan return cpu_asid(smp_processor_id(), gpa_mm); 48372582a6SJames Hogan } 49d7d5b05fSDeng-Cheng Zhu 5057e3869cSJames Hogan static int _kvm_mips_host_tlb_inv(unsigned long entryhi) 51d7d5b05fSDeng-Cheng Zhu { 52d7d5b05fSDeng-Cheng Zhu int idx; 53d7d5b05fSDeng-Cheng Zhu 5457e3869cSJames Hogan write_c0_entryhi(entryhi); 55d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 56d7d5b05fSDeng-Cheng Zhu 57d7d5b05fSDeng-Cheng Zhu tlb_probe(); 58d7d5b05fSDeng-Cheng Zhu tlb_probe_hazard(); 59d7d5b05fSDeng-Cheng Zhu idx = read_c0_index(); 60d7d5b05fSDeng-Cheng Zhu 61*a2cdc24eSzhouchuangao BUG_ON(idx >= current_cpu_data.tlbsize); 62d7d5b05fSDeng-Cheng Zhu 63f3a8603fSJames Hogan if (idx >= 0) { 64d7d5b05fSDeng-Cheng Zhu write_c0_entryhi(UNIQUE_ENTRYHI(idx)); 65d7d5b05fSDeng-Cheng Zhu write_c0_entrylo0(0); 66d7d5b05fSDeng-Cheng Zhu write_c0_entrylo1(0); 67d7d5b05fSDeng-Cheng Zhu mtc0_tlbw_hazard(); 68d7d5b05fSDeng-Cheng Zhu 69d7d5b05fSDeng-Cheng Zhu tlb_write_indexed(); 70138f7ad9SJames Hogan tlbw_use_hazard(); 71d7d5b05fSDeng-Cheng Zhu } 72d7d5b05fSDeng-Cheng Zhu 7357e3869cSJames Hogan return idx; 7457e3869cSJames Hogan } 7557e3869cSJames Hogan 76372582a6SJames Hogan /* GuestID management */ 77372582a6SJames Hogan 78372582a6SJames Hogan /** 79372582a6SJames Hogan * clear_root_gid() - Set GuestCtl1.RID for normal root operation. 80372582a6SJames Hogan */ 81372582a6SJames Hogan static inline void clear_root_gid(void) 82372582a6SJames Hogan { 83372582a6SJames Hogan if (cpu_has_guestid) { 84372582a6SJames Hogan clear_c0_guestctl1(MIPS_GCTL1_RID); 85372582a6SJames Hogan mtc0_tlbw_hazard(); 86372582a6SJames Hogan } 87372582a6SJames Hogan } 88372582a6SJames Hogan 89372582a6SJames Hogan /** 90372582a6SJames Hogan * set_root_gid_to_guest_gid() - Set GuestCtl1.RID to match GuestCtl1.ID. 91372582a6SJames Hogan * 92372582a6SJames Hogan * Sets the root GuestID to match the current guest GuestID, for TLB operation 93372582a6SJames Hogan * on the GPA->RPA mappings in the root TLB. 94372582a6SJames Hogan * 95372582a6SJames Hogan * The caller must be sure to disable HTW while the root GID is set, and 96372582a6SJames Hogan * possibly longer if TLB registers are modified. 97372582a6SJames Hogan */ 98372582a6SJames Hogan static inline void set_root_gid_to_guest_gid(void) 99372582a6SJames Hogan { 100372582a6SJames Hogan unsigned int guestctl1; 101372582a6SJames Hogan 102372582a6SJames Hogan if (cpu_has_guestid) { 103372582a6SJames Hogan back_to_back_c0_hazard(); 104372582a6SJames Hogan guestctl1 = read_c0_guestctl1(); 105372582a6SJames Hogan guestctl1 = (guestctl1 & ~MIPS_GCTL1_RID) | 106372582a6SJames Hogan ((guestctl1 & MIPS_GCTL1_ID) >> MIPS_GCTL1_ID_SHIFT) 107372582a6SJames Hogan << MIPS_GCTL1_RID_SHIFT; 108372582a6SJames Hogan write_c0_guestctl1(guestctl1); 109372582a6SJames Hogan mtc0_tlbw_hazard(); 110372582a6SJames Hogan } 111372582a6SJames Hogan } 112372582a6SJames Hogan 113372582a6SJames Hogan int kvm_vz_host_tlb_inv(struct kvm_vcpu *vcpu, unsigned long va) 114372582a6SJames Hogan { 115372582a6SJames Hogan int idx; 116372582a6SJames Hogan unsigned long flags, old_entryhi; 117372582a6SJames Hogan 118372582a6SJames Hogan local_irq_save(flags); 119372582a6SJames Hogan htw_stop(); 120372582a6SJames Hogan 121372582a6SJames Hogan /* Set root GuestID for root probe and write of guest TLB entry */ 122372582a6SJames Hogan set_root_gid_to_guest_gid(); 123372582a6SJames Hogan 124372582a6SJames Hogan old_entryhi = read_c0_entryhi(); 125372582a6SJames Hogan 126372582a6SJames Hogan idx = _kvm_mips_host_tlb_inv((va & VPN2_MASK) | 127372582a6SJames Hogan kvm_mips_get_root_asid(vcpu)); 128372582a6SJames Hogan 129372582a6SJames Hogan write_c0_entryhi(old_entryhi); 130372582a6SJames Hogan clear_root_gid(); 131372582a6SJames Hogan mtc0_tlbw_hazard(); 132372582a6SJames Hogan 133372582a6SJames Hogan htw_start(); 134372582a6SJames Hogan local_irq_restore(flags); 135372582a6SJames Hogan 1361c506c9cSJames Hogan /* 1371c506c9cSJames Hogan * We don't want to get reserved instruction exceptions for missing tlb 1381c506c9cSJames Hogan * entries. 1391c506c9cSJames Hogan */ 1401c506c9cSJames Hogan if (cpu_has_vtag_icache) 1411c506c9cSJames Hogan flush_icache_all(); 1421c506c9cSJames Hogan 143372582a6SJames Hogan if (idx > 0) 144372582a6SJames Hogan kvm_debug("%s: Invalidated root entryhi %#lx @ idx %d\n", 145372582a6SJames Hogan __func__, (va & VPN2_MASK) | 146372582a6SJames Hogan kvm_mips_get_root_asid(vcpu), idx); 147372582a6SJames Hogan 148372582a6SJames Hogan return 0; 149372582a6SJames Hogan } 150372582a6SJames Hogan EXPORT_SYMBOL_GPL(kvm_vz_host_tlb_inv); 151372582a6SJames Hogan 152372582a6SJames Hogan /** 153372582a6SJames Hogan * kvm_vz_guest_tlb_lookup() - Lookup a guest VZ TLB mapping. 154372582a6SJames Hogan * @vcpu: KVM VCPU pointer. 155372582a6SJames Hogan * @gpa: Guest virtual address in a TLB mapped guest segment. 156372582a6SJames Hogan * @gpa: Ponter to output guest physical address it maps to. 157372582a6SJames Hogan * 158372582a6SJames Hogan * Converts a guest virtual address in a guest TLB mapped segment to a guest 159372582a6SJames Hogan * physical address, by probing the guest TLB. 160372582a6SJames Hogan * 161372582a6SJames Hogan * Returns: 0 if guest TLB mapping exists for @gva. *@gpa will have been 162372582a6SJames Hogan * written. 163372582a6SJames Hogan * -EFAULT if no guest TLB mapping exists for @gva. *@gpa may not 164372582a6SJames Hogan * have been written. 165372582a6SJames Hogan */ 166372582a6SJames Hogan int kvm_vz_guest_tlb_lookup(struct kvm_vcpu *vcpu, unsigned long gva, 167372582a6SJames Hogan unsigned long *gpa) 168372582a6SJames Hogan { 169372582a6SJames Hogan unsigned long o_entryhi, o_entrylo[2], o_pagemask; 170372582a6SJames Hogan unsigned int o_index; 171372582a6SJames Hogan unsigned long entrylo[2], pagemask, pagemaskbit, pa; 172372582a6SJames Hogan unsigned long flags; 173372582a6SJames Hogan int index; 174372582a6SJames Hogan 175372582a6SJames Hogan /* Probe the guest TLB for a mapping */ 176372582a6SJames Hogan local_irq_save(flags); 177372582a6SJames Hogan /* Set root GuestID for root probe of guest TLB entry */ 178372582a6SJames Hogan htw_stop(); 179372582a6SJames Hogan set_root_gid_to_guest_gid(); 180372582a6SJames Hogan 181372582a6SJames Hogan o_entryhi = read_gc0_entryhi(); 182372582a6SJames Hogan o_index = read_gc0_index(); 183372582a6SJames Hogan 184372582a6SJames Hogan write_gc0_entryhi((o_entryhi & 0x3ff) | (gva & ~0xfffl)); 185372582a6SJames Hogan mtc0_tlbw_hazard(); 186372582a6SJames Hogan guest_tlb_probe(); 187372582a6SJames Hogan tlb_probe_hazard(); 188372582a6SJames Hogan 189372582a6SJames Hogan index = read_gc0_index(); 190372582a6SJames Hogan if (index < 0) { 191372582a6SJames Hogan /* No match, fail */ 192372582a6SJames Hogan write_gc0_entryhi(o_entryhi); 193372582a6SJames Hogan write_gc0_index(o_index); 194372582a6SJames Hogan 195372582a6SJames Hogan clear_root_gid(); 196372582a6SJames Hogan htw_start(); 197372582a6SJames Hogan local_irq_restore(flags); 198372582a6SJames Hogan return -EFAULT; 199372582a6SJames Hogan } 200372582a6SJames Hogan 201372582a6SJames Hogan /* Match! read the TLB entry */ 202372582a6SJames Hogan o_entrylo[0] = read_gc0_entrylo0(); 203372582a6SJames Hogan o_entrylo[1] = read_gc0_entrylo1(); 204372582a6SJames Hogan o_pagemask = read_gc0_pagemask(); 205372582a6SJames Hogan 206372582a6SJames Hogan mtc0_tlbr_hazard(); 207372582a6SJames Hogan guest_tlb_read(); 208372582a6SJames Hogan tlb_read_hazard(); 209372582a6SJames Hogan 210372582a6SJames Hogan entrylo[0] = read_gc0_entrylo0(); 211372582a6SJames Hogan entrylo[1] = read_gc0_entrylo1(); 212372582a6SJames Hogan pagemask = ~read_gc0_pagemask() & ~0x1fffl; 213372582a6SJames Hogan 214372582a6SJames Hogan write_gc0_entryhi(o_entryhi); 215372582a6SJames Hogan write_gc0_index(o_index); 216372582a6SJames Hogan write_gc0_entrylo0(o_entrylo[0]); 217372582a6SJames Hogan write_gc0_entrylo1(o_entrylo[1]); 218372582a6SJames Hogan write_gc0_pagemask(o_pagemask); 219372582a6SJames Hogan 220372582a6SJames Hogan clear_root_gid(); 221372582a6SJames Hogan htw_start(); 222372582a6SJames Hogan local_irq_restore(flags); 223372582a6SJames Hogan 224372582a6SJames Hogan /* Select one of the EntryLo values and interpret the GPA */ 225372582a6SJames Hogan pagemaskbit = (pagemask ^ (pagemask & (pagemask - 1))) >> 1; 226372582a6SJames Hogan pa = entrylo[!!(gva & pagemaskbit)]; 227372582a6SJames Hogan 228372582a6SJames Hogan /* 229372582a6SJames Hogan * TLB entry may have become invalid since TLB probe if physical FTLB 230372582a6SJames Hogan * entries are shared between threads (e.g. I6400). 231372582a6SJames Hogan */ 232372582a6SJames Hogan if (!(pa & ENTRYLO_V)) 233372582a6SJames Hogan return -EFAULT; 234372582a6SJames Hogan 235372582a6SJames Hogan /* 236372582a6SJames Hogan * Note, this doesn't take guest MIPS32 XPA into account, where PFN is 237372582a6SJames Hogan * split with XI/RI in the middle. 238372582a6SJames Hogan */ 239372582a6SJames Hogan pa = (pa << 6) & ~0xfffl; 240372582a6SJames Hogan pa |= gva & ~(pagemask | pagemaskbit); 241372582a6SJames Hogan 242372582a6SJames Hogan *gpa = pa; 243372582a6SJames Hogan return 0; 244372582a6SJames Hogan } 245372582a6SJames Hogan EXPORT_SYMBOL_GPL(kvm_vz_guest_tlb_lookup); 246372582a6SJames Hogan 247372582a6SJames Hogan /** 248372582a6SJames Hogan * kvm_vz_local_flush_roottlb_all_guests() - Flush all root TLB entries for 249372582a6SJames Hogan * guests. 250372582a6SJames Hogan * 251372582a6SJames Hogan * Invalidate all entries in root tlb which are GPA mappings. 252372582a6SJames Hogan */ 253372582a6SJames Hogan void kvm_vz_local_flush_roottlb_all_guests(void) 254372582a6SJames Hogan { 255372582a6SJames Hogan unsigned long flags; 256372582a6SJames Hogan unsigned long old_entryhi, old_pagemask, old_guestctl1; 257372582a6SJames Hogan int entry; 258372582a6SJames Hogan 259372582a6SJames Hogan if (WARN_ON(!cpu_has_guestid)) 260372582a6SJames Hogan return; 261372582a6SJames Hogan 262372582a6SJames Hogan local_irq_save(flags); 263372582a6SJames Hogan htw_stop(); 264372582a6SJames Hogan 265372582a6SJames Hogan /* TLBR may clobber EntryHi.ASID, PageMask, and GuestCtl1.RID */ 266372582a6SJames Hogan old_entryhi = read_c0_entryhi(); 267372582a6SJames Hogan old_pagemask = read_c0_pagemask(); 268372582a6SJames Hogan old_guestctl1 = read_c0_guestctl1(); 269372582a6SJames Hogan 270372582a6SJames Hogan /* 271372582a6SJames Hogan * Invalidate guest entries in root TLB while leaving root entries 272372582a6SJames Hogan * intact when possible. 273372582a6SJames Hogan */ 274372582a6SJames Hogan for (entry = 0; entry < current_cpu_data.tlbsize; entry++) { 275372582a6SJames Hogan write_c0_index(entry); 276372582a6SJames Hogan mtc0_tlbw_hazard(); 277372582a6SJames Hogan tlb_read(); 278372582a6SJames Hogan tlb_read_hazard(); 279372582a6SJames Hogan 280372582a6SJames Hogan /* Don't invalidate non-guest (RVA) mappings in the root TLB */ 281372582a6SJames Hogan if (!(read_c0_guestctl1() & MIPS_GCTL1_RID)) 282372582a6SJames Hogan continue; 283372582a6SJames Hogan 284372582a6SJames Hogan /* Make sure all entries differ. */ 285372582a6SJames Hogan write_c0_entryhi(UNIQUE_ENTRYHI(entry)); 286372582a6SJames Hogan write_c0_entrylo0(0); 287372582a6SJames Hogan write_c0_entrylo1(0); 288372582a6SJames Hogan write_c0_guestctl1(0); 289372582a6SJames Hogan mtc0_tlbw_hazard(); 290372582a6SJames Hogan tlb_write_indexed(); 291372582a6SJames Hogan } 292372582a6SJames Hogan 293372582a6SJames Hogan write_c0_entryhi(old_entryhi); 294372582a6SJames Hogan write_c0_pagemask(old_pagemask); 295372582a6SJames Hogan write_c0_guestctl1(old_guestctl1); 296372582a6SJames Hogan tlbw_use_hazard(); 297372582a6SJames Hogan 298372582a6SJames Hogan htw_start(); 299372582a6SJames Hogan local_irq_restore(flags); 300372582a6SJames Hogan } 301372582a6SJames Hogan EXPORT_SYMBOL_GPL(kvm_vz_local_flush_roottlb_all_guests); 302372582a6SJames Hogan 303372582a6SJames Hogan /** 304372582a6SJames Hogan * kvm_vz_local_flush_guesttlb_all() - Flush all guest TLB entries. 305372582a6SJames Hogan * 306372582a6SJames Hogan * Invalidate all entries in guest tlb irrespective of guestid. 307372582a6SJames Hogan */ 308372582a6SJames Hogan void kvm_vz_local_flush_guesttlb_all(void) 309372582a6SJames Hogan { 310372582a6SJames Hogan unsigned long flags; 311372582a6SJames Hogan unsigned long old_index; 312372582a6SJames Hogan unsigned long old_entryhi; 313372582a6SJames Hogan unsigned long old_entrylo[2]; 314372582a6SJames Hogan unsigned long old_pagemask; 315372582a6SJames Hogan int entry; 316824533adSJames Hogan u64 cvmmemctl2 = 0; 317372582a6SJames Hogan 318372582a6SJames Hogan local_irq_save(flags); 319372582a6SJames Hogan 320372582a6SJames Hogan /* Preserve all clobbered guest registers */ 321372582a6SJames Hogan old_index = read_gc0_index(); 322372582a6SJames Hogan old_entryhi = read_gc0_entryhi(); 323372582a6SJames Hogan old_entrylo[0] = read_gc0_entrylo0(); 324372582a6SJames Hogan old_entrylo[1] = read_gc0_entrylo1(); 325372582a6SJames Hogan old_pagemask = read_gc0_pagemask(); 326372582a6SJames Hogan 327824533adSJames Hogan switch (current_cpu_type()) { 328824533adSJames Hogan case CPU_CAVIUM_OCTEON3: 329824533adSJames Hogan /* Inhibit machine check due to multiple matching TLB entries */ 330824533adSJames Hogan cvmmemctl2 = read_c0_cvmmemctl2(); 331824533adSJames Hogan cvmmemctl2 |= CVMMEMCTL2_INHIBITTS; 332824533adSJames Hogan write_c0_cvmmemctl2(cvmmemctl2); 333824533adSJames Hogan break; 3347ff1f626SJason Yan } 335824533adSJames Hogan 336372582a6SJames Hogan /* Invalidate guest entries in guest TLB */ 337372582a6SJames Hogan write_gc0_entrylo0(0); 338372582a6SJames Hogan write_gc0_entrylo1(0); 339372582a6SJames Hogan write_gc0_pagemask(0); 340372582a6SJames Hogan for (entry = 0; entry < current_cpu_data.guest.tlbsize; entry++) { 341372582a6SJames Hogan /* Make sure all entries differ. */ 342372582a6SJames Hogan write_gc0_index(entry); 343372582a6SJames Hogan write_gc0_entryhi(UNIQUE_GUEST_ENTRYHI(entry)); 344372582a6SJames Hogan mtc0_tlbw_hazard(); 345372582a6SJames Hogan guest_tlb_write_indexed(); 346372582a6SJames Hogan } 347824533adSJames Hogan 348824533adSJames Hogan if (cvmmemctl2) { 349824533adSJames Hogan cvmmemctl2 &= ~CVMMEMCTL2_INHIBITTS; 350824533adSJames Hogan write_c0_cvmmemctl2(cvmmemctl2); 3517ff1f626SJason Yan } 352824533adSJames Hogan 353372582a6SJames Hogan write_gc0_index(old_index); 354372582a6SJames Hogan write_gc0_entryhi(old_entryhi); 355372582a6SJames Hogan write_gc0_entrylo0(old_entrylo[0]); 356372582a6SJames Hogan write_gc0_entrylo1(old_entrylo[1]); 357372582a6SJames Hogan write_gc0_pagemask(old_pagemask); 358372582a6SJames Hogan tlbw_use_hazard(); 359372582a6SJames Hogan 360372582a6SJames Hogan local_irq_restore(flags); 361372582a6SJames Hogan } 362372582a6SJames Hogan EXPORT_SYMBOL_GPL(kvm_vz_local_flush_guesttlb_all); 363372582a6SJames Hogan 364372582a6SJames Hogan /** 365372582a6SJames Hogan * kvm_vz_save_guesttlb() - Save a range of guest TLB entries. 366372582a6SJames Hogan * @buf: Buffer to write TLB entries into. 367372582a6SJames Hogan * @index: Start index. 368372582a6SJames Hogan * @count: Number of entries to save. 369372582a6SJames Hogan * 370372582a6SJames Hogan * Save a range of guest TLB entries. The caller must ensure interrupts are 371372582a6SJames Hogan * disabled. 372372582a6SJames Hogan */ 373372582a6SJames Hogan void kvm_vz_save_guesttlb(struct kvm_mips_tlb *buf, unsigned int index, 374372582a6SJames Hogan unsigned int count) 375372582a6SJames Hogan { 376372582a6SJames Hogan unsigned int end = index + count; 377372582a6SJames Hogan unsigned long old_entryhi, old_entrylo0, old_entrylo1, old_pagemask; 378372582a6SJames Hogan unsigned int guestctl1 = 0; 379372582a6SJames Hogan int old_index, i; 380372582a6SJames Hogan 381372582a6SJames Hogan /* Save registers we're about to clobber */ 382372582a6SJames Hogan old_index = read_gc0_index(); 383372582a6SJames Hogan old_entryhi = read_gc0_entryhi(); 384372582a6SJames Hogan old_entrylo0 = read_gc0_entrylo0(); 385372582a6SJames Hogan old_entrylo1 = read_gc0_entrylo1(); 386372582a6SJames Hogan old_pagemask = read_gc0_pagemask(); 387372582a6SJames Hogan 388372582a6SJames Hogan /* Set root GuestID for root probe */ 389372582a6SJames Hogan htw_stop(); 390372582a6SJames Hogan set_root_gid_to_guest_gid(); 391372582a6SJames Hogan if (cpu_has_guestid) 392372582a6SJames Hogan guestctl1 = read_c0_guestctl1(); 393372582a6SJames Hogan 394372582a6SJames Hogan /* Read each entry from guest TLB */ 395372582a6SJames Hogan for (i = index; i < end; ++i, ++buf) { 396372582a6SJames Hogan write_gc0_index(i); 397372582a6SJames Hogan 398372582a6SJames Hogan mtc0_tlbr_hazard(); 399372582a6SJames Hogan guest_tlb_read(); 400372582a6SJames Hogan tlb_read_hazard(); 401372582a6SJames Hogan 402372582a6SJames Hogan if (cpu_has_guestid && 403372582a6SJames Hogan (read_c0_guestctl1() ^ guestctl1) & MIPS_GCTL1_RID) { 404372582a6SJames Hogan /* Entry invalid or belongs to another guest */ 405372582a6SJames Hogan buf->tlb_hi = UNIQUE_GUEST_ENTRYHI(i); 406372582a6SJames Hogan buf->tlb_lo[0] = 0; 407372582a6SJames Hogan buf->tlb_lo[1] = 0; 408372582a6SJames Hogan buf->tlb_mask = 0; 409372582a6SJames Hogan } else { 410372582a6SJames Hogan /* Entry belongs to the right guest */ 411372582a6SJames Hogan buf->tlb_hi = read_gc0_entryhi(); 412372582a6SJames Hogan buf->tlb_lo[0] = read_gc0_entrylo0(); 413372582a6SJames Hogan buf->tlb_lo[1] = read_gc0_entrylo1(); 414372582a6SJames Hogan buf->tlb_mask = read_gc0_pagemask(); 415372582a6SJames Hogan } 416372582a6SJames Hogan } 417372582a6SJames Hogan 418372582a6SJames Hogan /* Clear root GuestID again */ 419372582a6SJames Hogan clear_root_gid(); 420372582a6SJames Hogan htw_start(); 421372582a6SJames Hogan 422372582a6SJames Hogan /* Restore clobbered registers */ 423372582a6SJames Hogan write_gc0_index(old_index); 424372582a6SJames Hogan write_gc0_entryhi(old_entryhi); 425372582a6SJames Hogan write_gc0_entrylo0(old_entrylo0); 426372582a6SJames Hogan write_gc0_entrylo1(old_entrylo1); 427372582a6SJames Hogan write_gc0_pagemask(old_pagemask); 428372582a6SJames Hogan 429372582a6SJames Hogan tlbw_use_hazard(); 430372582a6SJames Hogan } 431372582a6SJames Hogan EXPORT_SYMBOL_GPL(kvm_vz_save_guesttlb); 432372582a6SJames Hogan 433372582a6SJames Hogan /** 434372582a6SJames Hogan * kvm_vz_load_guesttlb() - Save a range of guest TLB entries. 435372582a6SJames Hogan * @buf: Buffer to read TLB entries from. 436372582a6SJames Hogan * @index: Start index. 437372582a6SJames Hogan * @count: Number of entries to load. 438372582a6SJames Hogan * 439372582a6SJames Hogan * Load a range of guest TLB entries. The caller must ensure interrupts are 440372582a6SJames Hogan * disabled. 441372582a6SJames Hogan */ 442372582a6SJames Hogan void kvm_vz_load_guesttlb(const struct kvm_mips_tlb *buf, unsigned int index, 443372582a6SJames Hogan unsigned int count) 444372582a6SJames Hogan { 445372582a6SJames Hogan unsigned int end = index + count; 446372582a6SJames Hogan unsigned long old_entryhi, old_entrylo0, old_entrylo1, old_pagemask; 447372582a6SJames Hogan int old_index, i; 448372582a6SJames Hogan 449372582a6SJames Hogan /* Save registers we're about to clobber */ 450372582a6SJames Hogan old_index = read_gc0_index(); 451372582a6SJames Hogan old_entryhi = read_gc0_entryhi(); 452372582a6SJames Hogan old_entrylo0 = read_gc0_entrylo0(); 453372582a6SJames Hogan old_entrylo1 = read_gc0_entrylo1(); 454372582a6SJames Hogan old_pagemask = read_gc0_pagemask(); 455372582a6SJames Hogan 456372582a6SJames Hogan /* Set root GuestID for root probe */ 457372582a6SJames Hogan htw_stop(); 458372582a6SJames Hogan set_root_gid_to_guest_gid(); 459372582a6SJames Hogan 460372582a6SJames Hogan /* Write each entry to guest TLB */ 461372582a6SJames Hogan for (i = index; i < end; ++i, ++buf) { 462372582a6SJames Hogan write_gc0_index(i); 463372582a6SJames Hogan write_gc0_entryhi(buf->tlb_hi); 464372582a6SJames Hogan write_gc0_entrylo0(buf->tlb_lo[0]); 465372582a6SJames Hogan write_gc0_entrylo1(buf->tlb_lo[1]); 466372582a6SJames Hogan write_gc0_pagemask(buf->tlb_mask); 467372582a6SJames Hogan 468372582a6SJames Hogan mtc0_tlbw_hazard(); 469372582a6SJames Hogan guest_tlb_write_indexed(); 470372582a6SJames Hogan } 471372582a6SJames Hogan 472372582a6SJames Hogan /* Clear root GuestID again */ 473372582a6SJames Hogan clear_root_gid(); 474372582a6SJames Hogan htw_start(); 475372582a6SJames Hogan 476372582a6SJames Hogan /* Restore clobbered registers */ 477372582a6SJames Hogan write_gc0_index(old_index); 478372582a6SJames Hogan write_gc0_entryhi(old_entryhi); 479372582a6SJames Hogan write_gc0_entrylo0(old_entrylo0); 480372582a6SJames Hogan write_gc0_entrylo1(old_entrylo1); 481372582a6SJames Hogan write_gc0_pagemask(old_pagemask); 482372582a6SJames Hogan 483372582a6SJames Hogan tlbw_use_hazard(); 484372582a6SJames Hogan } 485372582a6SJames Hogan EXPORT_SYMBOL_GPL(kvm_vz_load_guesttlb); 486372582a6SJames Hogan 4878a5097eeSHuacai Chen #ifdef CONFIG_CPU_LOONGSON64 4888a5097eeSHuacai Chen void kvm_loongson_clear_guest_vtlb(void) 4898a5097eeSHuacai Chen { 4908a5097eeSHuacai Chen int idx = read_gc0_index(); 4918a5097eeSHuacai Chen 4928a5097eeSHuacai Chen /* Set root GuestID for root probe and write of guest TLB entry */ 4938a5097eeSHuacai Chen set_root_gid_to_guest_gid(); 4948a5097eeSHuacai Chen 4958a5097eeSHuacai Chen write_gc0_index(0); 4968a5097eeSHuacai Chen guest_tlbinvf(); 4978a5097eeSHuacai Chen write_gc0_index(idx); 4988a5097eeSHuacai Chen 4998a5097eeSHuacai Chen clear_root_gid(); 5008a5097eeSHuacai Chen set_c0_diag(LOONGSON_DIAG_ITLB | LOONGSON_DIAG_DTLB); 5018a5097eeSHuacai Chen } 5028a5097eeSHuacai Chen EXPORT_SYMBOL_GPL(kvm_loongson_clear_guest_vtlb); 5038a5097eeSHuacai Chen 5048a5097eeSHuacai Chen void kvm_loongson_clear_guest_ftlb(void) 5058a5097eeSHuacai Chen { 5068a5097eeSHuacai Chen int i; 5078a5097eeSHuacai Chen int idx = read_gc0_index(); 5088a5097eeSHuacai Chen 5098a5097eeSHuacai Chen /* Set root GuestID for root probe and write of guest TLB entry */ 5108a5097eeSHuacai Chen set_root_gid_to_guest_gid(); 5118a5097eeSHuacai Chen 5128a5097eeSHuacai Chen for (i = current_cpu_data.tlbsizevtlb; 5138a5097eeSHuacai Chen i < (current_cpu_data.tlbsizevtlb + 5148a5097eeSHuacai Chen current_cpu_data.tlbsizeftlbsets); 5158a5097eeSHuacai Chen i++) { 5168a5097eeSHuacai Chen write_gc0_index(i); 5178a5097eeSHuacai Chen guest_tlbinvf(); 5188a5097eeSHuacai Chen } 5198a5097eeSHuacai Chen write_gc0_index(idx); 5208a5097eeSHuacai Chen 5218a5097eeSHuacai Chen clear_root_gid(); 5228a5097eeSHuacai Chen set_c0_diag(LOONGSON_DIAG_ITLB | LOONGSON_DIAG_DTLB); 5238a5097eeSHuacai Chen } 5248a5097eeSHuacai Chen EXPORT_SYMBOL_GPL(kvm_loongson_clear_guest_ftlb); 5258a5097eeSHuacai Chen #endif 526