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