0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013 #include <linux/sched.h>
0014 #include <linux/smp.h>
0015 #include <linux/mm.h>
0016 #include <linux/delay.h>
0017 #include <linux/export.h>
0018 #include <linux/kvm_host.h>
0019 #include <linux/srcu.h>
0020
0021 #include <asm/cpu.h>
0022 #include <asm/bootinfo.h>
0023 #include <asm/mipsregs.h>
0024 #include <asm/mmu_context.h>
0025 #include <asm/cacheflush.h>
0026 #include <asm/tlb.h>
0027 #include <asm/tlbdebug.h>
0028
0029 #undef CONFIG_MIPS_MT
0030 #include <asm/r4kcache.h>
0031 #define CONFIG_MIPS_MT
0032
0033 unsigned long GUESTID_MASK;
0034 EXPORT_SYMBOL_GPL(GUESTID_MASK);
0035 unsigned long GUESTID_FIRST_VERSION;
0036 EXPORT_SYMBOL_GPL(GUESTID_FIRST_VERSION);
0037 unsigned long GUESTID_VERSION_MASK;
0038 EXPORT_SYMBOL_GPL(GUESTID_VERSION_MASK);
0039
0040 static u32 kvm_mips_get_root_asid(struct kvm_vcpu *vcpu)
0041 {
0042 struct mm_struct *gpa_mm = &vcpu->kvm->arch.gpa_mm;
0043
0044 if (cpu_has_guestid)
0045 return 0;
0046 else
0047 return cpu_asid(smp_processor_id(), gpa_mm);
0048 }
0049
0050 static int _kvm_mips_host_tlb_inv(unsigned long entryhi)
0051 {
0052 int idx;
0053
0054 write_c0_entryhi(entryhi);
0055 mtc0_tlbw_hazard();
0056
0057 tlb_probe();
0058 tlb_probe_hazard();
0059 idx = read_c0_index();
0060
0061 BUG_ON(idx >= current_cpu_data.tlbsize);
0062
0063 if (idx >= 0) {
0064 write_c0_entryhi(UNIQUE_ENTRYHI(idx));
0065 write_c0_entrylo0(0);
0066 write_c0_entrylo1(0);
0067 mtc0_tlbw_hazard();
0068
0069 tlb_write_indexed();
0070 tlbw_use_hazard();
0071 }
0072
0073 return idx;
0074 }
0075
0076
0077
0078
0079
0080
0081 static inline void clear_root_gid(void)
0082 {
0083 if (cpu_has_guestid) {
0084 clear_c0_guestctl1(MIPS_GCTL1_RID);
0085 mtc0_tlbw_hazard();
0086 }
0087 }
0088
0089
0090
0091
0092
0093
0094
0095
0096
0097
0098 static inline void set_root_gid_to_guest_gid(void)
0099 {
0100 unsigned int guestctl1;
0101
0102 if (cpu_has_guestid) {
0103 back_to_back_c0_hazard();
0104 guestctl1 = read_c0_guestctl1();
0105 guestctl1 = (guestctl1 & ~MIPS_GCTL1_RID) |
0106 ((guestctl1 & MIPS_GCTL1_ID) >> MIPS_GCTL1_ID_SHIFT)
0107 << MIPS_GCTL1_RID_SHIFT;
0108 write_c0_guestctl1(guestctl1);
0109 mtc0_tlbw_hazard();
0110 }
0111 }
0112
0113 int kvm_vz_host_tlb_inv(struct kvm_vcpu *vcpu, unsigned long va)
0114 {
0115 int idx;
0116 unsigned long flags, old_entryhi;
0117
0118 local_irq_save(flags);
0119 htw_stop();
0120
0121
0122 set_root_gid_to_guest_gid();
0123
0124 old_entryhi = read_c0_entryhi();
0125
0126 idx = _kvm_mips_host_tlb_inv((va & VPN2_MASK) |
0127 kvm_mips_get_root_asid(vcpu));
0128
0129 write_c0_entryhi(old_entryhi);
0130 clear_root_gid();
0131 mtc0_tlbw_hazard();
0132
0133 htw_start();
0134 local_irq_restore(flags);
0135
0136
0137
0138
0139
0140 if (cpu_has_vtag_icache)
0141 flush_icache_all();
0142
0143 if (idx > 0)
0144 kvm_debug("%s: Invalidated root entryhi %#lx @ idx %d\n",
0145 __func__, (va & VPN2_MASK) |
0146 kvm_mips_get_root_asid(vcpu), idx);
0147
0148 return 0;
0149 }
0150 EXPORT_SYMBOL_GPL(kvm_vz_host_tlb_inv);
0151
0152
0153
0154
0155
0156
0157
0158
0159
0160
0161
0162
0163
0164
0165
0166 int kvm_vz_guest_tlb_lookup(struct kvm_vcpu *vcpu, unsigned long gva,
0167 unsigned long *gpa)
0168 {
0169 unsigned long o_entryhi, o_entrylo[2], o_pagemask;
0170 unsigned int o_index;
0171 unsigned long entrylo[2], pagemask, pagemaskbit, pa;
0172 unsigned long flags;
0173 int index;
0174
0175
0176 local_irq_save(flags);
0177
0178 htw_stop();
0179 set_root_gid_to_guest_gid();
0180
0181 o_entryhi = read_gc0_entryhi();
0182 o_index = read_gc0_index();
0183
0184 write_gc0_entryhi((o_entryhi & 0x3ff) | (gva & ~0xfffl));
0185 mtc0_tlbw_hazard();
0186 guest_tlb_probe();
0187 tlb_probe_hazard();
0188
0189 index = read_gc0_index();
0190 if (index < 0) {
0191
0192 write_gc0_entryhi(o_entryhi);
0193 write_gc0_index(o_index);
0194
0195 clear_root_gid();
0196 htw_start();
0197 local_irq_restore(flags);
0198 return -EFAULT;
0199 }
0200
0201
0202 o_entrylo[0] = read_gc0_entrylo0();
0203 o_entrylo[1] = read_gc0_entrylo1();
0204 o_pagemask = read_gc0_pagemask();
0205
0206 mtc0_tlbr_hazard();
0207 guest_tlb_read();
0208 tlb_read_hazard();
0209
0210 entrylo[0] = read_gc0_entrylo0();
0211 entrylo[1] = read_gc0_entrylo1();
0212 pagemask = ~read_gc0_pagemask() & ~0x1fffl;
0213
0214 write_gc0_entryhi(o_entryhi);
0215 write_gc0_index(o_index);
0216 write_gc0_entrylo0(o_entrylo[0]);
0217 write_gc0_entrylo1(o_entrylo[1]);
0218 write_gc0_pagemask(o_pagemask);
0219
0220 clear_root_gid();
0221 htw_start();
0222 local_irq_restore(flags);
0223
0224
0225 pagemaskbit = (pagemask ^ (pagemask & (pagemask - 1))) >> 1;
0226 pa = entrylo[!!(gva & pagemaskbit)];
0227
0228
0229
0230
0231
0232 if (!(pa & ENTRYLO_V))
0233 return -EFAULT;
0234
0235
0236
0237
0238
0239 pa = (pa << 6) & ~0xfffl;
0240 pa |= gva & ~(pagemask | pagemaskbit);
0241
0242 *gpa = pa;
0243 return 0;
0244 }
0245 EXPORT_SYMBOL_GPL(kvm_vz_guest_tlb_lookup);
0246
0247
0248
0249
0250
0251
0252
0253 void kvm_vz_local_flush_roottlb_all_guests(void)
0254 {
0255 unsigned long flags;
0256 unsigned long old_entryhi, old_pagemask, old_guestctl1;
0257 int entry;
0258
0259 if (WARN_ON(!cpu_has_guestid))
0260 return;
0261
0262 local_irq_save(flags);
0263 htw_stop();
0264
0265
0266 old_entryhi = read_c0_entryhi();
0267 old_pagemask = read_c0_pagemask();
0268 old_guestctl1 = read_c0_guestctl1();
0269
0270
0271
0272
0273
0274 for (entry = 0; entry < current_cpu_data.tlbsize; entry++) {
0275 write_c0_index(entry);
0276 mtc0_tlbw_hazard();
0277 tlb_read();
0278 tlb_read_hazard();
0279
0280
0281 if (!(read_c0_guestctl1() & MIPS_GCTL1_RID))
0282 continue;
0283
0284
0285 write_c0_entryhi(UNIQUE_ENTRYHI(entry));
0286 write_c0_entrylo0(0);
0287 write_c0_entrylo1(0);
0288 write_c0_guestctl1(0);
0289 mtc0_tlbw_hazard();
0290 tlb_write_indexed();
0291 }
0292
0293 write_c0_entryhi(old_entryhi);
0294 write_c0_pagemask(old_pagemask);
0295 write_c0_guestctl1(old_guestctl1);
0296 tlbw_use_hazard();
0297
0298 htw_start();
0299 local_irq_restore(flags);
0300 }
0301 EXPORT_SYMBOL_GPL(kvm_vz_local_flush_roottlb_all_guests);
0302
0303
0304
0305
0306
0307
0308 void kvm_vz_local_flush_guesttlb_all(void)
0309 {
0310 unsigned long flags;
0311 unsigned long old_index;
0312 unsigned long old_entryhi;
0313 unsigned long old_entrylo[2];
0314 unsigned long old_pagemask;
0315 int entry;
0316 u64 cvmmemctl2 = 0;
0317
0318 local_irq_save(flags);
0319
0320
0321 old_index = read_gc0_index();
0322 old_entryhi = read_gc0_entryhi();
0323 old_entrylo[0] = read_gc0_entrylo0();
0324 old_entrylo[1] = read_gc0_entrylo1();
0325 old_pagemask = read_gc0_pagemask();
0326
0327 switch (current_cpu_type()) {
0328 case CPU_CAVIUM_OCTEON3:
0329
0330 cvmmemctl2 = read_c0_cvmmemctl2();
0331 cvmmemctl2 |= CVMMEMCTL2_INHIBITTS;
0332 write_c0_cvmmemctl2(cvmmemctl2);
0333 break;
0334 }
0335
0336
0337 write_gc0_entrylo0(0);
0338 write_gc0_entrylo1(0);
0339 write_gc0_pagemask(0);
0340 for (entry = 0; entry < current_cpu_data.guest.tlbsize; entry++) {
0341
0342 write_gc0_index(entry);
0343 write_gc0_entryhi(UNIQUE_GUEST_ENTRYHI(entry));
0344 mtc0_tlbw_hazard();
0345 guest_tlb_write_indexed();
0346 }
0347
0348 if (cvmmemctl2) {
0349 cvmmemctl2 &= ~CVMMEMCTL2_INHIBITTS;
0350 write_c0_cvmmemctl2(cvmmemctl2);
0351 }
0352
0353 write_gc0_index(old_index);
0354 write_gc0_entryhi(old_entryhi);
0355 write_gc0_entrylo0(old_entrylo[0]);
0356 write_gc0_entrylo1(old_entrylo[1]);
0357 write_gc0_pagemask(old_pagemask);
0358 tlbw_use_hazard();
0359
0360 local_irq_restore(flags);
0361 }
0362 EXPORT_SYMBOL_GPL(kvm_vz_local_flush_guesttlb_all);
0363
0364
0365
0366
0367
0368
0369
0370
0371
0372
0373 void kvm_vz_save_guesttlb(struct kvm_mips_tlb *buf, unsigned int index,
0374 unsigned int count)
0375 {
0376 unsigned int end = index + count;
0377 unsigned long old_entryhi, old_entrylo0, old_entrylo1, old_pagemask;
0378 unsigned int guestctl1 = 0;
0379 int old_index, i;
0380
0381
0382 old_index = read_gc0_index();
0383 old_entryhi = read_gc0_entryhi();
0384 old_entrylo0 = read_gc0_entrylo0();
0385 old_entrylo1 = read_gc0_entrylo1();
0386 old_pagemask = read_gc0_pagemask();
0387
0388
0389 htw_stop();
0390 set_root_gid_to_guest_gid();
0391 if (cpu_has_guestid)
0392 guestctl1 = read_c0_guestctl1();
0393
0394
0395 for (i = index; i < end; ++i, ++buf) {
0396 write_gc0_index(i);
0397
0398 mtc0_tlbr_hazard();
0399 guest_tlb_read();
0400 tlb_read_hazard();
0401
0402 if (cpu_has_guestid &&
0403 (read_c0_guestctl1() ^ guestctl1) & MIPS_GCTL1_RID) {
0404
0405 buf->tlb_hi = UNIQUE_GUEST_ENTRYHI(i);
0406 buf->tlb_lo[0] = 0;
0407 buf->tlb_lo[1] = 0;
0408 buf->tlb_mask = 0;
0409 } else {
0410
0411 buf->tlb_hi = read_gc0_entryhi();
0412 buf->tlb_lo[0] = read_gc0_entrylo0();
0413 buf->tlb_lo[1] = read_gc0_entrylo1();
0414 buf->tlb_mask = read_gc0_pagemask();
0415 }
0416 }
0417
0418
0419 clear_root_gid();
0420 htw_start();
0421
0422
0423 write_gc0_index(old_index);
0424 write_gc0_entryhi(old_entryhi);
0425 write_gc0_entrylo0(old_entrylo0);
0426 write_gc0_entrylo1(old_entrylo1);
0427 write_gc0_pagemask(old_pagemask);
0428
0429 tlbw_use_hazard();
0430 }
0431 EXPORT_SYMBOL_GPL(kvm_vz_save_guesttlb);
0432
0433
0434
0435
0436
0437
0438
0439
0440
0441
0442 void kvm_vz_load_guesttlb(const struct kvm_mips_tlb *buf, unsigned int index,
0443 unsigned int count)
0444 {
0445 unsigned int end = index + count;
0446 unsigned long old_entryhi, old_entrylo0, old_entrylo1, old_pagemask;
0447 int old_index, i;
0448
0449
0450 old_index = read_gc0_index();
0451 old_entryhi = read_gc0_entryhi();
0452 old_entrylo0 = read_gc0_entrylo0();
0453 old_entrylo1 = read_gc0_entrylo1();
0454 old_pagemask = read_gc0_pagemask();
0455
0456
0457 htw_stop();
0458 set_root_gid_to_guest_gid();
0459
0460
0461 for (i = index; i < end; ++i, ++buf) {
0462 write_gc0_index(i);
0463 write_gc0_entryhi(buf->tlb_hi);
0464 write_gc0_entrylo0(buf->tlb_lo[0]);
0465 write_gc0_entrylo1(buf->tlb_lo[1]);
0466 write_gc0_pagemask(buf->tlb_mask);
0467
0468 mtc0_tlbw_hazard();
0469 guest_tlb_write_indexed();
0470 }
0471
0472
0473 clear_root_gid();
0474 htw_start();
0475
0476
0477 write_gc0_index(old_index);
0478 write_gc0_entryhi(old_entryhi);
0479 write_gc0_entrylo0(old_entrylo0);
0480 write_gc0_entrylo1(old_entrylo1);
0481 write_gc0_pagemask(old_pagemask);
0482
0483 tlbw_use_hazard();
0484 }
0485 EXPORT_SYMBOL_GPL(kvm_vz_load_guesttlb);
0486
0487 #ifdef CONFIG_CPU_LOONGSON64
0488 void kvm_loongson_clear_guest_vtlb(void)
0489 {
0490 int idx = read_gc0_index();
0491
0492
0493 set_root_gid_to_guest_gid();
0494
0495 write_gc0_index(0);
0496 guest_tlbinvf();
0497 write_gc0_index(idx);
0498
0499 clear_root_gid();
0500 set_c0_diag(LOONGSON_DIAG_ITLB | LOONGSON_DIAG_DTLB);
0501 }
0502 EXPORT_SYMBOL_GPL(kvm_loongson_clear_guest_vtlb);
0503
0504 void kvm_loongson_clear_guest_ftlb(void)
0505 {
0506 int i;
0507 int idx = read_gc0_index();
0508
0509
0510 set_root_gid_to_guest_gid();
0511
0512 for (i = current_cpu_data.tlbsizevtlb;
0513 i < (current_cpu_data.tlbsizevtlb +
0514 current_cpu_data.tlbsizeftlbsets);
0515 i++) {
0516 write_gc0_index(i);
0517 guest_tlbinvf();
0518 }
0519 write_gc0_index(idx);
0520
0521 clear_root_gid();
0522 set_c0_diag(LOONGSON_DIAG_ITLB | LOONGSON_DIAG_DTLB);
0523 }
0524 EXPORT_SYMBOL_GPL(kvm_loongson_clear_guest_ftlb);
0525 #endif