162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 362306a36Sopenharmony_ci * License. See the file "COPYING" in the main directory of this archive 462306a36Sopenharmony_ci * for more details. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * KVM/MIPS TLB handling, this file is part of the Linux host kernel so that 762306a36Sopenharmony_ci * TLB handlers run from KSEG0 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Copyright (C) 2012 MIPS Technologies, Inc. All rights reserved. 1062306a36Sopenharmony_ci * Authors: Sanjay Lal <sanjayl@kymasys.com> 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/sched.h> 1462306a36Sopenharmony_ci#include <linux/smp.h> 1562306a36Sopenharmony_ci#include <linux/mm.h> 1662306a36Sopenharmony_ci#include <linux/delay.h> 1762306a36Sopenharmony_ci#include <linux/export.h> 1862306a36Sopenharmony_ci#include <linux/kvm_host.h> 1962306a36Sopenharmony_ci#include <linux/srcu.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include <asm/cpu.h> 2262306a36Sopenharmony_ci#include <asm/bootinfo.h> 2362306a36Sopenharmony_ci#include <asm/mipsregs.h> 2462306a36Sopenharmony_ci#include <asm/mmu_context.h> 2562306a36Sopenharmony_ci#include <asm/cacheflush.h> 2662306a36Sopenharmony_ci#include <asm/tlb.h> 2762306a36Sopenharmony_ci#include <asm/tlbdebug.h> 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#undef CONFIG_MIPS_MT 3062306a36Sopenharmony_ci#include <asm/r4kcache.h> 3162306a36Sopenharmony_ci#define CONFIG_MIPS_MT 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ciunsigned long GUESTID_MASK; 3462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(GUESTID_MASK); 3562306a36Sopenharmony_ciunsigned long GUESTID_FIRST_VERSION; 3662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(GUESTID_FIRST_VERSION); 3762306a36Sopenharmony_ciunsigned long GUESTID_VERSION_MASK; 3862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(GUESTID_VERSION_MASK); 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistatic u32 kvm_mips_get_root_asid(struct kvm_vcpu *vcpu) 4162306a36Sopenharmony_ci{ 4262306a36Sopenharmony_ci struct mm_struct *gpa_mm = &vcpu->kvm->arch.gpa_mm; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci if (cpu_has_guestid) 4562306a36Sopenharmony_ci return 0; 4662306a36Sopenharmony_ci else 4762306a36Sopenharmony_ci return cpu_asid(smp_processor_id(), gpa_mm); 4862306a36Sopenharmony_ci} 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic int _kvm_mips_host_tlb_inv(unsigned long entryhi) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci int idx; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci write_c0_entryhi(entryhi); 5562306a36Sopenharmony_ci mtc0_tlbw_hazard(); 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci tlb_probe(); 5862306a36Sopenharmony_ci tlb_probe_hazard(); 5962306a36Sopenharmony_ci idx = read_c0_index(); 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci BUG_ON(idx >= current_cpu_data.tlbsize); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci if (idx >= 0) { 6462306a36Sopenharmony_ci write_c0_entryhi(UNIQUE_ENTRYHI(idx)); 6562306a36Sopenharmony_ci write_c0_entrylo0(0); 6662306a36Sopenharmony_ci write_c0_entrylo1(0); 6762306a36Sopenharmony_ci mtc0_tlbw_hazard(); 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci tlb_write_indexed(); 7062306a36Sopenharmony_ci tlbw_use_hazard(); 7162306a36Sopenharmony_ci } 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci return idx; 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci/* GuestID management */ 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci/** 7962306a36Sopenharmony_ci * clear_root_gid() - Set GuestCtl1.RID for normal root operation. 8062306a36Sopenharmony_ci */ 8162306a36Sopenharmony_cistatic inline void clear_root_gid(void) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci if (cpu_has_guestid) { 8462306a36Sopenharmony_ci clear_c0_guestctl1(MIPS_GCTL1_RID); 8562306a36Sopenharmony_ci mtc0_tlbw_hazard(); 8662306a36Sopenharmony_ci } 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci/** 9062306a36Sopenharmony_ci * set_root_gid_to_guest_gid() - Set GuestCtl1.RID to match GuestCtl1.ID. 9162306a36Sopenharmony_ci * 9262306a36Sopenharmony_ci * Sets the root GuestID to match the current guest GuestID, for TLB operation 9362306a36Sopenharmony_ci * on the GPA->RPA mappings in the root TLB. 9462306a36Sopenharmony_ci * 9562306a36Sopenharmony_ci * The caller must be sure to disable HTW while the root GID is set, and 9662306a36Sopenharmony_ci * possibly longer if TLB registers are modified. 9762306a36Sopenharmony_ci */ 9862306a36Sopenharmony_cistatic inline void set_root_gid_to_guest_gid(void) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci unsigned int guestctl1; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci if (cpu_has_guestid) { 10362306a36Sopenharmony_ci back_to_back_c0_hazard(); 10462306a36Sopenharmony_ci guestctl1 = read_c0_guestctl1(); 10562306a36Sopenharmony_ci guestctl1 = (guestctl1 & ~MIPS_GCTL1_RID) | 10662306a36Sopenharmony_ci ((guestctl1 & MIPS_GCTL1_ID) >> MIPS_GCTL1_ID_SHIFT) 10762306a36Sopenharmony_ci << MIPS_GCTL1_RID_SHIFT; 10862306a36Sopenharmony_ci write_c0_guestctl1(guestctl1); 10962306a36Sopenharmony_ci mtc0_tlbw_hazard(); 11062306a36Sopenharmony_ci } 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ciint kvm_vz_host_tlb_inv(struct kvm_vcpu *vcpu, unsigned long va) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci int idx; 11662306a36Sopenharmony_ci unsigned long flags, old_entryhi; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci local_irq_save(flags); 11962306a36Sopenharmony_ci htw_stop(); 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci /* Set root GuestID for root probe and write of guest TLB entry */ 12262306a36Sopenharmony_ci set_root_gid_to_guest_gid(); 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci old_entryhi = read_c0_entryhi(); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci idx = _kvm_mips_host_tlb_inv((va & VPN2_MASK) | 12762306a36Sopenharmony_ci kvm_mips_get_root_asid(vcpu)); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci write_c0_entryhi(old_entryhi); 13062306a36Sopenharmony_ci clear_root_gid(); 13162306a36Sopenharmony_ci mtc0_tlbw_hazard(); 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci htw_start(); 13462306a36Sopenharmony_ci local_irq_restore(flags); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci /* 13762306a36Sopenharmony_ci * We don't want to get reserved instruction exceptions for missing tlb 13862306a36Sopenharmony_ci * entries. 13962306a36Sopenharmony_ci */ 14062306a36Sopenharmony_ci if (cpu_has_vtag_icache) 14162306a36Sopenharmony_ci flush_icache_all(); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci if (idx > 0) 14462306a36Sopenharmony_ci kvm_debug("%s: Invalidated root entryhi %#lx @ idx %d\n", 14562306a36Sopenharmony_ci __func__, (va & VPN2_MASK) | 14662306a36Sopenharmony_ci kvm_mips_get_root_asid(vcpu), idx); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci return 0; 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(kvm_vz_host_tlb_inv); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci/** 15362306a36Sopenharmony_ci * kvm_vz_guest_tlb_lookup() - Lookup a guest VZ TLB mapping. 15462306a36Sopenharmony_ci * @vcpu: KVM VCPU pointer. 15562306a36Sopenharmony_ci * @gpa: Guest virtual address in a TLB mapped guest segment. 15662306a36Sopenharmony_ci * @gpa: Pointer to output guest physical address it maps to. 15762306a36Sopenharmony_ci * 15862306a36Sopenharmony_ci * Converts a guest virtual address in a guest TLB mapped segment to a guest 15962306a36Sopenharmony_ci * physical address, by probing the guest TLB. 16062306a36Sopenharmony_ci * 16162306a36Sopenharmony_ci * Returns: 0 if guest TLB mapping exists for @gva. *@gpa will have been 16262306a36Sopenharmony_ci * written. 16362306a36Sopenharmony_ci * -EFAULT if no guest TLB mapping exists for @gva. *@gpa may not 16462306a36Sopenharmony_ci * have been written. 16562306a36Sopenharmony_ci */ 16662306a36Sopenharmony_ciint kvm_vz_guest_tlb_lookup(struct kvm_vcpu *vcpu, unsigned long gva, 16762306a36Sopenharmony_ci unsigned long *gpa) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci unsigned long o_entryhi, o_entrylo[2], o_pagemask; 17062306a36Sopenharmony_ci unsigned int o_index; 17162306a36Sopenharmony_ci unsigned long entrylo[2], pagemask, pagemaskbit, pa; 17262306a36Sopenharmony_ci unsigned long flags; 17362306a36Sopenharmony_ci int index; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci /* Probe the guest TLB for a mapping */ 17662306a36Sopenharmony_ci local_irq_save(flags); 17762306a36Sopenharmony_ci /* Set root GuestID for root probe of guest TLB entry */ 17862306a36Sopenharmony_ci htw_stop(); 17962306a36Sopenharmony_ci set_root_gid_to_guest_gid(); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci o_entryhi = read_gc0_entryhi(); 18262306a36Sopenharmony_ci o_index = read_gc0_index(); 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci write_gc0_entryhi((o_entryhi & 0x3ff) | (gva & ~0xfffl)); 18562306a36Sopenharmony_ci mtc0_tlbw_hazard(); 18662306a36Sopenharmony_ci guest_tlb_probe(); 18762306a36Sopenharmony_ci tlb_probe_hazard(); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci index = read_gc0_index(); 19062306a36Sopenharmony_ci if (index < 0) { 19162306a36Sopenharmony_ci /* No match, fail */ 19262306a36Sopenharmony_ci write_gc0_entryhi(o_entryhi); 19362306a36Sopenharmony_ci write_gc0_index(o_index); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci clear_root_gid(); 19662306a36Sopenharmony_ci htw_start(); 19762306a36Sopenharmony_ci local_irq_restore(flags); 19862306a36Sopenharmony_ci return -EFAULT; 19962306a36Sopenharmony_ci } 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci /* Match! read the TLB entry */ 20262306a36Sopenharmony_ci o_entrylo[0] = read_gc0_entrylo0(); 20362306a36Sopenharmony_ci o_entrylo[1] = read_gc0_entrylo1(); 20462306a36Sopenharmony_ci o_pagemask = read_gc0_pagemask(); 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci mtc0_tlbr_hazard(); 20762306a36Sopenharmony_ci guest_tlb_read(); 20862306a36Sopenharmony_ci tlb_read_hazard(); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci entrylo[0] = read_gc0_entrylo0(); 21162306a36Sopenharmony_ci entrylo[1] = read_gc0_entrylo1(); 21262306a36Sopenharmony_ci pagemask = ~read_gc0_pagemask() & ~0x1fffl; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci write_gc0_entryhi(o_entryhi); 21562306a36Sopenharmony_ci write_gc0_index(o_index); 21662306a36Sopenharmony_ci write_gc0_entrylo0(o_entrylo[0]); 21762306a36Sopenharmony_ci write_gc0_entrylo1(o_entrylo[1]); 21862306a36Sopenharmony_ci write_gc0_pagemask(o_pagemask); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci clear_root_gid(); 22162306a36Sopenharmony_ci htw_start(); 22262306a36Sopenharmony_ci local_irq_restore(flags); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci /* Select one of the EntryLo values and interpret the GPA */ 22562306a36Sopenharmony_ci pagemaskbit = (pagemask ^ (pagemask & (pagemask - 1))) >> 1; 22662306a36Sopenharmony_ci pa = entrylo[!!(gva & pagemaskbit)]; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci /* 22962306a36Sopenharmony_ci * TLB entry may have become invalid since TLB probe if physical FTLB 23062306a36Sopenharmony_ci * entries are shared between threads (e.g. I6400). 23162306a36Sopenharmony_ci */ 23262306a36Sopenharmony_ci if (!(pa & ENTRYLO_V)) 23362306a36Sopenharmony_ci return -EFAULT; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci /* 23662306a36Sopenharmony_ci * Note, this doesn't take guest MIPS32 XPA into account, where PFN is 23762306a36Sopenharmony_ci * split with XI/RI in the middle. 23862306a36Sopenharmony_ci */ 23962306a36Sopenharmony_ci pa = (pa << 6) & ~0xfffl; 24062306a36Sopenharmony_ci pa |= gva & ~(pagemask | pagemaskbit); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci *gpa = pa; 24362306a36Sopenharmony_ci return 0; 24462306a36Sopenharmony_ci} 24562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(kvm_vz_guest_tlb_lookup); 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci/** 24862306a36Sopenharmony_ci * kvm_vz_local_flush_roottlb_all_guests() - Flush all root TLB entries for 24962306a36Sopenharmony_ci * guests. 25062306a36Sopenharmony_ci * 25162306a36Sopenharmony_ci * Invalidate all entries in root tlb which are GPA mappings. 25262306a36Sopenharmony_ci */ 25362306a36Sopenharmony_civoid kvm_vz_local_flush_roottlb_all_guests(void) 25462306a36Sopenharmony_ci{ 25562306a36Sopenharmony_ci unsigned long flags; 25662306a36Sopenharmony_ci unsigned long old_entryhi, old_pagemask, old_guestctl1; 25762306a36Sopenharmony_ci int entry; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci if (WARN_ON(!cpu_has_guestid)) 26062306a36Sopenharmony_ci return; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci local_irq_save(flags); 26362306a36Sopenharmony_ci htw_stop(); 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci /* TLBR may clobber EntryHi.ASID, PageMask, and GuestCtl1.RID */ 26662306a36Sopenharmony_ci old_entryhi = read_c0_entryhi(); 26762306a36Sopenharmony_ci old_pagemask = read_c0_pagemask(); 26862306a36Sopenharmony_ci old_guestctl1 = read_c0_guestctl1(); 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci /* 27162306a36Sopenharmony_ci * Invalidate guest entries in root TLB while leaving root entries 27262306a36Sopenharmony_ci * intact when possible. 27362306a36Sopenharmony_ci */ 27462306a36Sopenharmony_ci for (entry = 0; entry < current_cpu_data.tlbsize; entry++) { 27562306a36Sopenharmony_ci write_c0_index(entry); 27662306a36Sopenharmony_ci mtc0_tlbw_hazard(); 27762306a36Sopenharmony_ci tlb_read(); 27862306a36Sopenharmony_ci tlb_read_hazard(); 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci /* Don't invalidate non-guest (RVA) mappings in the root TLB */ 28162306a36Sopenharmony_ci if (!(read_c0_guestctl1() & MIPS_GCTL1_RID)) 28262306a36Sopenharmony_ci continue; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci /* Make sure all entries differ. */ 28562306a36Sopenharmony_ci write_c0_entryhi(UNIQUE_ENTRYHI(entry)); 28662306a36Sopenharmony_ci write_c0_entrylo0(0); 28762306a36Sopenharmony_ci write_c0_entrylo1(0); 28862306a36Sopenharmony_ci write_c0_guestctl1(0); 28962306a36Sopenharmony_ci mtc0_tlbw_hazard(); 29062306a36Sopenharmony_ci tlb_write_indexed(); 29162306a36Sopenharmony_ci } 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci write_c0_entryhi(old_entryhi); 29462306a36Sopenharmony_ci write_c0_pagemask(old_pagemask); 29562306a36Sopenharmony_ci write_c0_guestctl1(old_guestctl1); 29662306a36Sopenharmony_ci tlbw_use_hazard(); 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci htw_start(); 29962306a36Sopenharmony_ci local_irq_restore(flags); 30062306a36Sopenharmony_ci} 30162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(kvm_vz_local_flush_roottlb_all_guests); 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci/** 30462306a36Sopenharmony_ci * kvm_vz_local_flush_guesttlb_all() - Flush all guest TLB entries. 30562306a36Sopenharmony_ci * 30662306a36Sopenharmony_ci * Invalidate all entries in guest tlb irrespective of guestid. 30762306a36Sopenharmony_ci */ 30862306a36Sopenharmony_civoid kvm_vz_local_flush_guesttlb_all(void) 30962306a36Sopenharmony_ci{ 31062306a36Sopenharmony_ci unsigned long flags; 31162306a36Sopenharmony_ci unsigned long old_index; 31262306a36Sopenharmony_ci unsigned long old_entryhi; 31362306a36Sopenharmony_ci unsigned long old_entrylo[2]; 31462306a36Sopenharmony_ci unsigned long old_pagemask; 31562306a36Sopenharmony_ci int entry; 31662306a36Sopenharmony_ci u64 cvmmemctl2 = 0; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci local_irq_save(flags); 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci /* Preserve all clobbered guest registers */ 32162306a36Sopenharmony_ci old_index = read_gc0_index(); 32262306a36Sopenharmony_ci old_entryhi = read_gc0_entryhi(); 32362306a36Sopenharmony_ci old_entrylo[0] = read_gc0_entrylo0(); 32462306a36Sopenharmony_ci old_entrylo[1] = read_gc0_entrylo1(); 32562306a36Sopenharmony_ci old_pagemask = read_gc0_pagemask(); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci switch (current_cpu_type()) { 32862306a36Sopenharmony_ci case CPU_CAVIUM_OCTEON3: 32962306a36Sopenharmony_ci /* Inhibit machine check due to multiple matching TLB entries */ 33062306a36Sopenharmony_ci cvmmemctl2 = read_c0_cvmmemctl2(); 33162306a36Sopenharmony_ci cvmmemctl2 |= CVMMEMCTL2_INHIBITTS; 33262306a36Sopenharmony_ci write_c0_cvmmemctl2(cvmmemctl2); 33362306a36Sopenharmony_ci break; 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci /* Invalidate guest entries in guest TLB */ 33762306a36Sopenharmony_ci write_gc0_entrylo0(0); 33862306a36Sopenharmony_ci write_gc0_entrylo1(0); 33962306a36Sopenharmony_ci write_gc0_pagemask(0); 34062306a36Sopenharmony_ci for (entry = 0; entry < current_cpu_data.guest.tlbsize; entry++) { 34162306a36Sopenharmony_ci /* Make sure all entries differ. */ 34262306a36Sopenharmony_ci write_gc0_index(entry); 34362306a36Sopenharmony_ci write_gc0_entryhi(UNIQUE_GUEST_ENTRYHI(entry)); 34462306a36Sopenharmony_ci mtc0_tlbw_hazard(); 34562306a36Sopenharmony_ci guest_tlb_write_indexed(); 34662306a36Sopenharmony_ci } 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci if (cvmmemctl2) { 34962306a36Sopenharmony_ci cvmmemctl2 &= ~CVMMEMCTL2_INHIBITTS; 35062306a36Sopenharmony_ci write_c0_cvmmemctl2(cvmmemctl2); 35162306a36Sopenharmony_ci } 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci write_gc0_index(old_index); 35462306a36Sopenharmony_ci write_gc0_entryhi(old_entryhi); 35562306a36Sopenharmony_ci write_gc0_entrylo0(old_entrylo[0]); 35662306a36Sopenharmony_ci write_gc0_entrylo1(old_entrylo[1]); 35762306a36Sopenharmony_ci write_gc0_pagemask(old_pagemask); 35862306a36Sopenharmony_ci tlbw_use_hazard(); 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci local_irq_restore(flags); 36162306a36Sopenharmony_ci} 36262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(kvm_vz_local_flush_guesttlb_all); 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci/** 36562306a36Sopenharmony_ci * kvm_vz_save_guesttlb() - Save a range of guest TLB entries. 36662306a36Sopenharmony_ci * @buf: Buffer to write TLB entries into. 36762306a36Sopenharmony_ci * @index: Start index. 36862306a36Sopenharmony_ci * @count: Number of entries to save. 36962306a36Sopenharmony_ci * 37062306a36Sopenharmony_ci * Save a range of guest TLB entries. The caller must ensure interrupts are 37162306a36Sopenharmony_ci * disabled. 37262306a36Sopenharmony_ci */ 37362306a36Sopenharmony_civoid kvm_vz_save_guesttlb(struct kvm_mips_tlb *buf, unsigned int index, 37462306a36Sopenharmony_ci unsigned int count) 37562306a36Sopenharmony_ci{ 37662306a36Sopenharmony_ci unsigned int end = index + count; 37762306a36Sopenharmony_ci unsigned long old_entryhi, old_entrylo0, old_entrylo1, old_pagemask; 37862306a36Sopenharmony_ci unsigned int guestctl1 = 0; 37962306a36Sopenharmony_ci int old_index, i; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci /* Save registers we're about to clobber */ 38262306a36Sopenharmony_ci old_index = read_gc0_index(); 38362306a36Sopenharmony_ci old_entryhi = read_gc0_entryhi(); 38462306a36Sopenharmony_ci old_entrylo0 = read_gc0_entrylo0(); 38562306a36Sopenharmony_ci old_entrylo1 = read_gc0_entrylo1(); 38662306a36Sopenharmony_ci old_pagemask = read_gc0_pagemask(); 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci /* Set root GuestID for root probe */ 38962306a36Sopenharmony_ci htw_stop(); 39062306a36Sopenharmony_ci set_root_gid_to_guest_gid(); 39162306a36Sopenharmony_ci if (cpu_has_guestid) 39262306a36Sopenharmony_ci guestctl1 = read_c0_guestctl1(); 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci /* Read each entry from guest TLB */ 39562306a36Sopenharmony_ci for (i = index; i < end; ++i, ++buf) { 39662306a36Sopenharmony_ci write_gc0_index(i); 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci mtc0_tlbr_hazard(); 39962306a36Sopenharmony_ci guest_tlb_read(); 40062306a36Sopenharmony_ci tlb_read_hazard(); 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci if (cpu_has_guestid && 40362306a36Sopenharmony_ci (read_c0_guestctl1() ^ guestctl1) & MIPS_GCTL1_RID) { 40462306a36Sopenharmony_ci /* Entry invalid or belongs to another guest */ 40562306a36Sopenharmony_ci buf->tlb_hi = UNIQUE_GUEST_ENTRYHI(i); 40662306a36Sopenharmony_ci buf->tlb_lo[0] = 0; 40762306a36Sopenharmony_ci buf->tlb_lo[1] = 0; 40862306a36Sopenharmony_ci buf->tlb_mask = 0; 40962306a36Sopenharmony_ci } else { 41062306a36Sopenharmony_ci /* Entry belongs to the right guest */ 41162306a36Sopenharmony_ci buf->tlb_hi = read_gc0_entryhi(); 41262306a36Sopenharmony_ci buf->tlb_lo[0] = read_gc0_entrylo0(); 41362306a36Sopenharmony_ci buf->tlb_lo[1] = read_gc0_entrylo1(); 41462306a36Sopenharmony_ci buf->tlb_mask = read_gc0_pagemask(); 41562306a36Sopenharmony_ci } 41662306a36Sopenharmony_ci } 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci /* Clear root GuestID again */ 41962306a36Sopenharmony_ci clear_root_gid(); 42062306a36Sopenharmony_ci htw_start(); 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci /* Restore clobbered registers */ 42362306a36Sopenharmony_ci write_gc0_index(old_index); 42462306a36Sopenharmony_ci write_gc0_entryhi(old_entryhi); 42562306a36Sopenharmony_ci write_gc0_entrylo0(old_entrylo0); 42662306a36Sopenharmony_ci write_gc0_entrylo1(old_entrylo1); 42762306a36Sopenharmony_ci write_gc0_pagemask(old_pagemask); 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci tlbw_use_hazard(); 43062306a36Sopenharmony_ci} 43162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(kvm_vz_save_guesttlb); 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci/** 43462306a36Sopenharmony_ci * kvm_vz_load_guesttlb() - Save a range of guest TLB entries. 43562306a36Sopenharmony_ci * @buf: Buffer to read TLB entries from. 43662306a36Sopenharmony_ci * @index: Start index. 43762306a36Sopenharmony_ci * @count: Number of entries to load. 43862306a36Sopenharmony_ci * 43962306a36Sopenharmony_ci * Load a range of guest TLB entries. The caller must ensure interrupts are 44062306a36Sopenharmony_ci * disabled. 44162306a36Sopenharmony_ci */ 44262306a36Sopenharmony_civoid kvm_vz_load_guesttlb(const struct kvm_mips_tlb *buf, unsigned int index, 44362306a36Sopenharmony_ci unsigned int count) 44462306a36Sopenharmony_ci{ 44562306a36Sopenharmony_ci unsigned int end = index + count; 44662306a36Sopenharmony_ci unsigned long old_entryhi, old_entrylo0, old_entrylo1, old_pagemask; 44762306a36Sopenharmony_ci int old_index, i; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci /* Save registers we're about to clobber */ 45062306a36Sopenharmony_ci old_index = read_gc0_index(); 45162306a36Sopenharmony_ci old_entryhi = read_gc0_entryhi(); 45262306a36Sopenharmony_ci old_entrylo0 = read_gc0_entrylo0(); 45362306a36Sopenharmony_ci old_entrylo1 = read_gc0_entrylo1(); 45462306a36Sopenharmony_ci old_pagemask = read_gc0_pagemask(); 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci /* Set root GuestID for root probe */ 45762306a36Sopenharmony_ci htw_stop(); 45862306a36Sopenharmony_ci set_root_gid_to_guest_gid(); 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci /* Write each entry to guest TLB */ 46162306a36Sopenharmony_ci for (i = index; i < end; ++i, ++buf) { 46262306a36Sopenharmony_ci write_gc0_index(i); 46362306a36Sopenharmony_ci write_gc0_entryhi(buf->tlb_hi); 46462306a36Sopenharmony_ci write_gc0_entrylo0(buf->tlb_lo[0]); 46562306a36Sopenharmony_ci write_gc0_entrylo1(buf->tlb_lo[1]); 46662306a36Sopenharmony_ci write_gc0_pagemask(buf->tlb_mask); 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci mtc0_tlbw_hazard(); 46962306a36Sopenharmony_ci guest_tlb_write_indexed(); 47062306a36Sopenharmony_ci } 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci /* Clear root GuestID again */ 47362306a36Sopenharmony_ci clear_root_gid(); 47462306a36Sopenharmony_ci htw_start(); 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci /* Restore clobbered registers */ 47762306a36Sopenharmony_ci write_gc0_index(old_index); 47862306a36Sopenharmony_ci write_gc0_entryhi(old_entryhi); 47962306a36Sopenharmony_ci write_gc0_entrylo0(old_entrylo0); 48062306a36Sopenharmony_ci write_gc0_entrylo1(old_entrylo1); 48162306a36Sopenharmony_ci write_gc0_pagemask(old_pagemask); 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci tlbw_use_hazard(); 48462306a36Sopenharmony_ci} 48562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(kvm_vz_load_guesttlb); 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci#ifdef CONFIG_CPU_LOONGSON64 48862306a36Sopenharmony_civoid kvm_loongson_clear_guest_vtlb(void) 48962306a36Sopenharmony_ci{ 49062306a36Sopenharmony_ci int idx = read_gc0_index(); 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci /* Set root GuestID for root probe and write of guest TLB entry */ 49362306a36Sopenharmony_ci set_root_gid_to_guest_gid(); 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci write_gc0_index(0); 49662306a36Sopenharmony_ci guest_tlbinvf(); 49762306a36Sopenharmony_ci write_gc0_index(idx); 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci clear_root_gid(); 50062306a36Sopenharmony_ci set_c0_diag(LOONGSON_DIAG_ITLB | LOONGSON_DIAG_DTLB); 50162306a36Sopenharmony_ci} 50262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(kvm_loongson_clear_guest_vtlb); 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_civoid kvm_loongson_clear_guest_ftlb(void) 50562306a36Sopenharmony_ci{ 50662306a36Sopenharmony_ci int i; 50762306a36Sopenharmony_ci int idx = read_gc0_index(); 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci /* Set root GuestID for root probe and write of guest TLB entry */ 51062306a36Sopenharmony_ci set_root_gid_to_guest_gid(); 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci for (i = current_cpu_data.tlbsizevtlb; 51362306a36Sopenharmony_ci i < (current_cpu_data.tlbsizevtlb + 51462306a36Sopenharmony_ci current_cpu_data.tlbsizeftlbsets); 51562306a36Sopenharmony_ci i++) { 51662306a36Sopenharmony_ci write_gc0_index(i); 51762306a36Sopenharmony_ci guest_tlbinvf(); 51862306a36Sopenharmony_ci } 51962306a36Sopenharmony_ci write_gc0_index(idx); 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci clear_root_gid(); 52262306a36Sopenharmony_ci set_c0_diag(LOONGSON_DIAG_ITLB | LOONGSON_DIAG_DTLB); 52362306a36Sopenharmony_ci} 52462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(kvm_loongson_clear_guest_ftlb); 52562306a36Sopenharmony_ci#endif 526