xref: /linux/arch/mips/kvm/tlb.c (revision d7d5b05faf1679849c5220627c7263b4041e15ef)
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