aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJames Hogan <james.hogan@imgtec.com>2015-02-06 05:56:27 -0500
committerJames Hogan <james.hogan@imgtec.com>2015-03-27 17:25:20 -0400
commitc2537ed9fb8e17d713e5e67fcede047699d25814 (patch)
tree1a4d37e5ddfa872efda4ae2ea42c1655a27ec3d0
parent2b6009d646887cac8888f1ce8694af0beefce88b (diff)
MIPS: KVM: Add MSA exception handling
Add guest exception handling for MIPS SIMD Architecture (MSA) floating point exceptions and MSA disabled exceptions. MSA floating point exceptions from the guest need passing to the guest kernel, so for these a guest MSAFPE is emulated. MSA disabled exceptions are normally handled by passing a reserved instruction exception to the guest (because no guest MSA was supported), but the hypervisor can now handle them if the guest has MSA by passing an MSA disabled exception to the guest, or if the guest has MSA enabled by transparently restoring the guest MSA context and enabling MSA and the FPU. Signed-off-by: James Hogan <james.hogan@imgtec.com> Cc: Paolo Bonzini <pbonzini@redhat.com> Cc: Paul Burton <paul.burton@imgtec.com> Cc: Ralf Baechle <ralf@linux-mips.org> Cc: Gleb Natapov <gleb@kernel.org> Cc: linux-mips@linux-mips.org Cc: kvm@vger.kernel.org
-rw-r--r--arch/mips/include/asm/kvm_host.h16
-rw-r--r--arch/mips/kvm/emulate.c71
-rw-r--r--arch/mips/kvm/mips.c10
-rw-r--r--arch/mips/kvm/stats.c2
-rw-r--r--arch/mips/kvm/trap_emul.c43
5 files changed, 140 insertions, 2 deletions
diff --git a/arch/mips/include/asm/kvm_host.h b/arch/mips/include/asm/kvm_host.h
index 1dc0dca15cbd..4c25823563fe 100644
--- a/arch/mips/include/asm/kvm_host.h
+++ b/arch/mips/include/asm/kvm_host.h
@@ -123,7 +123,9 @@ struct kvm_vcpu_stat {
123 u32 resvd_inst_exits; 123 u32 resvd_inst_exits;
124 u32 break_inst_exits; 124 u32 break_inst_exits;
125 u32 trap_inst_exits; 125 u32 trap_inst_exits;
126 u32 msa_fpe_exits;
126 u32 fpe_exits; 127 u32 fpe_exits;
128 u32 msa_disabled_exits;
127 u32 flush_dcache_exits; 129 u32 flush_dcache_exits;
128 u32 halt_successful_poll; 130 u32 halt_successful_poll;
129 u32 halt_wakeup; 131 u32 halt_wakeup;
@@ -144,7 +146,9 @@ enum kvm_mips_exit_types {
144 RESVD_INST_EXITS, 146 RESVD_INST_EXITS,
145 BREAK_INST_EXITS, 147 BREAK_INST_EXITS,
146 TRAP_INST_EXITS, 148 TRAP_INST_EXITS,
149 MSA_FPE_EXITS,
147 FPE_EXITS, 150 FPE_EXITS,
151 MSA_DISABLED_EXITS,
148 FLUSH_DCACHE_EXITS, 152 FLUSH_DCACHE_EXITS,
149 MAX_KVM_MIPS_EXIT_TYPES 153 MAX_KVM_MIPS_EXIT_TYPES
150}; 154};
@@ -305,6 +309,7 @@ enum mips_mmu_types {
305 */ 309 */
306#define T_TRAP 13 /* Trap instruction */ 310#define T_TRAP 13 /* Trap instruction */
307#define T_VCEI 14 /* Virtual coherency exception */ 311#define T_VCEI 14 /* Virtual coherency exception */
312#define T_MSAFPE 14 /* MSA floating point exception */
308#define T_FPE 15 /* Floating point exception */ 313#define T_FPE 15 /* Floating point exception */
309#define T_MSADIS 21 /* MSA disabled exception */ 314#define T_MSADIS 21 /* MSA disabled exception */
310#define T_WATCH 23 /* Watch address reference */ 315#define T_WATCH 23 /* Watch address reference */
@@ -601,6 +606,7 @@ struct kvm_mips_callbacks {
601 int (*handle_res_inst)(struct kvm_vcpu *vcpu); 606 int (*handle_res_inst)(struct kvm_vcpu *vcpu);
602 int (*handle_break)(struct kvm_vcpu *vcpu); 607 int (*handle_break)(struct kvm_vcpu *vcpu);
603 int (*handle_trap)(struct kvm_vcpu *vcpu); 608 int (*handle_trap)(struct kvm_vcpu *vcpu);
609 int (*handle_msa_fpe)(struct kvm_vcpu *vcpu);
604 int (*handle_fpe)(struct kvm_vcpu *vcpu); 610 int (*handle_fpe)(struct kvm_vcpu *vcpu);
605 int (*handle_msa_disabled)(struct kvm_vcpu *vcpu); 611 int (*handle_msa_disabled)(struct kvm_vcpu *vcpu);
606 int (*vm_init)(struct kvm *kvm); 612 int (*vm_init)(struct kvm *kvm);
@@ -756,11 +762,21 @@ extern enum emulation_result kvm_mips_emulate_trap_exc(unsigned long cause,
756 struct kvm_run *run, 762 struct kvm_run *run,
757 struct kvm_vcpu *vcpu); 763 struct kvm_vcpu *vcpu);
758 764
765extern enum emulation_result kvm_mips_emulate_msafpe_exc(unsigned long cause,
766 uint32_t *opc,
767 struct kvm_run *run,
768 struct kvm_vcpu *vcpu);
769
759extern enum emulation_result kvm_mips_emulate_fpe_exc(unsigned long cause, 770extern enum emulation_result kvm_mips_emulate_fpe_exc(unsigned long cause,
760 uint32_t *opc, 771 uint32_t *opc,
761 struct kvm_run *run, 772 struct kvm_run *run,
762 struct kvm_vcpu *vcpu); 773 struct kvm_vcpu *vcpu);
763 774
775extern enum emulation_result kvm_mips_emulate_msadis_exc(unsigned long cause,
776 uint32_t *opc,
777 struct kvm_run *run,
778 struct kvm_vcpu *vcpu);
779
764extern enum emulation_result kvm_mips_complete_mmio_load(struct kvm_vcpu *vcpu, 780extern enum emulation_result kvm_mips_complete_mmio_load(struct kvm_vcpu *vcpu,
765 struct kvm_run *run); 781 struct kvm_run *run);
766 782
diff --git a/arch/mips/kvm/emulate.c b/arch/mips/kvm/emulate.c
index 07f554c72cb8..6230f376a44e 100644
--- a/arch/mips/kvm/emulate.c
+++ b/arch/mips/kvm/emulate.c
@@ -2179,6 +2179,41 @@ enum emulation_result kvm_mips_emulate_trap_exc(unsigned long cause,
2179 return er; 2179 return er;
2180} 2180}
2181 2181
2182enum emulation_result kvm_mips_emulate_msafpe_exc(unsigned long cause,
2183 uint32_t *opc,
2184 struct kvm_run *run,
2185 struct kvm_vcpu *vcpu)
2186{
2187 struct mips_coproc *cop0 = vcpu->arch.cop0;
2188 struct kvm_vcpu_arch *arch = &vcpu->arch;
2189 enum emulation_result er = EMULATE_DONE;
2190
2191 if ((kvm_read_c0_guest_status(cop0) & ST0_EXL) == 0) {
2192 /* save old pc */
2193 kvm_write_c0_guest_epc(cop0, arch->pc);
2194 kvm_set_c0_guest_status(cop0, ST0_EXL);
2195
2196 if (cause & CAUSEF_BD)
2197 kvm_set_c0_guest_cause(cop0, CAUSEF_BD);
2198 else
2199 kvm_clear_c0_guest_cause(cop0, CAUSEF_BD);
2200
2201 kvm_debug("Delivering MSAFPE @ pc %#lx\n", arch->pc);
2202
2203 kvm_change_c0_guest_cause(cop0, (0xff),
2204 (T_MSAFPE << CAUSEB_EXCCODE));
2205
2206 /* Set PC to the exception entry point */
2207 arch->pc = KVM_GUEST_KSEG0 + 0x180;
2208
2209 } else {
2210 kvm_err("Trying to deliver MSAFPE when EXL is already set\n");
2211 er = EMULATE_FAIL;
2212 }
2213
2214 return er;
2215}
2216
2182enum emulation_result kvm_mips_emulate_fpe_exc(unsigned long cause, 2217enum emulation_result kvm_mips_emulate_fpe_exc(unsigned long cause,
2183 uint32_t *opc, 2218 uint32_t *opc,
2184 struct kvm_run *run, 2219 struct kvm_run *run,
@@ -2214,6 +2249,41 @@ enum emulation_result kvm_mips_emulate_fpe_exc(unsigned long cause,
2214 return er; 2249 return er;
2215} 2250}
2216 2251
2252enum emulation_result kvm_mips_emulate_msadis_exc(unsigned long cause,
2253 uint32_t *opc,
2254 struct kvm_run *run,
2255 struct kvm_vcpu *vcpu)
2256{
2257 struct mips_coproc *cop0 = vcpu->arch.cop0;
2258 struct kvm_vcpu_arch *arch = &vcpu->arch;
2259 enum emulation_result er = EMULATE_DONE;
2260
2261 if ((kvm_read_c0_guest_status(cop0) & ST0_EXL) == 0) {
2262 /* save old pc */
2263 kvm_write_c0_guest_epc(cop0, arch->pc);
2264 kvm_set_c0_guest_status(cop0, ST0_EXL);
2265
2266 if (cause & CAUSEF_BD)
2267 kvm_set_c0_guest_cause(cop0, CAUSEF_BD);
2268 else
2269 kvm_clear_c0_guest_cause(cop0, CAUSEF_BD);
2270
2271 kvm_debug("Delivering MSADIS @ pc %#lx\n", arch->pc);
2272
2273 kvm_change_c0_guest_cause(cop0, (0xff),
2274 (T_MSADIS << CAUSEB_EXCCODE));
2275
2276 /* Set PC to the exception entry point */
2277 arch->pc = KVM_GUEST_KSEG0 + 0x180;
2278
2279 } else {
2280 kvm_err("Trying to deliver MSADIS when EXL is already set\n");
2281 er = EMULATE_FAIL;
2282 }
2283
2284 return er;
2285}
2286
2217/* ll/sc, rdhwr, sync emulation */ 2287/* ll/sc, rdhwr, sync emulation */
2218 2288
2219#define OPCODE 0xfc000000 2289#define OPCODE 0xfc000000
@@ -2421,6 +2491,7 @@ enum emulation_result kvm_mips_check_privilege(unsigned long cause,
2421 case T_BREAK: 2491 case T_BREAK:
2422 case T_RES_INST: 2492 case T_RES_INST:
2423 case T_TRAP: 2493 case T_TRAP:
2494 case T_MSAFPE:
2424 case T_FPE: 2495 case T_FPE:
2425 case T_MSADIS: 2496 case T_MSADIS:
2426 break; 2497 break;
diff --git a/arch/mips/kvm/mips.c b/arch/mips/kvm/mips.c
index a17f21015a0b..e02c7e5a12ff 100644
--- a/arch/mips/kvm/mips.c
+++ b/arch/mips/kvm/mips.c
@@ -50,7 +50,9 @@ struct kvm_stats_debugfs_item debugfs_entries[] = {
50 { "resvd_inst", VCPU_STAT(resvd_inst_exits), KVM_STAT_VCPU }, 50 { "resvd_inst", VCPU_STAT(resvd_inst_exits), KVM_STAT_VCPU },
51 { "break_inst", VCPU_STAT(break_inst_exits), KVM_STAT_VCPU }, 51 { "break_inst", VCPU_STAT(break_inst_exits), KVM_STAT_VCPU },
52 { "trap_inst", VCPU_STAT(trap_inst_exits), KVM_STAT_VCPU }, 52 { "trap_inst", VCPU_STAT(trap_inst_exits), KVM_STAT_VCPU },
53 { "msa_fpe", VCPU_STAT(msa_fpe_exits), KVM_STAT_VCPU },
53 { "fpe", VCPU_STAT(fpe_exits), KVM_STAT_VCPU }, 54 { "fpe", VCPU_STAT(fpe_exits), KVM_STAT_VCPU },
55 { "msa_disabled", VCPU_STAT(msa_disabled_exits), KVM_STAT_VCPU },
54 { "flush_dcache", VCPU_STAT(flush_dcache_exits), KVM_STAT_VCPU }, 56 { "flush_dcache", VCPU_STAT(flush_dcache_exits), KVM_STAT_VCPU },
55 { "halt_successful_poll", VCPU_STAT(halt_successful_poll), KVM_STAT_VCPU }, 57 { "halt_successful_poll", VCPU_STAT(halt_successful_poll), KVM_STAT_VCPU },
56 { "halt_wakeup", VCPU_STAT(halt_wakeup), KVM_STAT_VCPU }, 58 { "halt_wakeup", VCPU_STAT(halt_wakeup), KVM_STAT_VCPU },
@@ -1256,6 +1258,12 @@ int kvm_mips_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu)
1256 ret = kvm_mips_callbacks->handle_trap(vcpu); 1258 ret = kvm_mips_callbacks->handle_trap(vcpu);
1257 break; 1259 break;
1258 1260
1261 case T_MSAFPE:
1262 ++vcpu->stat.msa_fpe_exits;
1263 trace_kvm_exit(vcpu, MSA_FPE_EXITS);
1264 ret = kvm_mips_callbacks->handle_msa_fpe(vcpu);
1265 break;
1266
1259 case T_FPE: 1267 case T_FPE:
1260 ++vcpu->stat.fpe_exits; 1268 ++vcpu->stat.fpe_exits;
1261 trace_kvm_exit(vcpu, FPE_EXITS); 1269 trace_kvm_exit(vcpu, FPE_EXITS);
@@ -1263,6 +1271,8 @@ int kvm_mips_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu)
1263 break; 1271 break;
1264 1272
1265 case T_MSADIS: 1273 case T_MSADIS:
1274 ++vcpu->stat.msa_disabled_exits;
1275 trace_kvm_exit(vcpu, MSA_DISABLED_EXITS);
1266 ret = kvm_mips_callbacks->handle_msa_disabled(vcpu); 1276 ret = kvm_mips_callbacks->handle_msa_disabled(vcpu);
1267 break; 1277 break;
1268 1278
diff --git a/arch/mips/kvm/stats.c b/arch/mips/kvm/stats.c
index 3843828f3b91..888bb67070ac 100644
--- a/arch/mips/kvm/stats.c
+++ b/arch/mips/kvm/stats.c
@@ -26,7 +26,9 @@ char *kvm_mips_exit_types_str[MAX_KVM_MIPS_EXIT_TYPES] = {
26 "Reserved Inst", 26 "Reserved Inst",
27 "Break Inst", 27 "Break Inst",
28 "Trap Inst", 28 "Trap Inst",
29 "MSA FPE",
29 "FPE", 30 "FPE",
31 "MSA Disabled",
30 "D-Cache Flushes", 32 "D-Cache Flushes",
31}; 33};
32 34
diff --git a/arch/mips/kvm/trap_emul.c b/arch/mips/kvm/trap_emul.c
index 421d5b815f24..d836ed5b0bc7 100644
--- a/arch/mips/kvm/trap_emul.c
+++ b/arch/mips/kvm/trap_emul.c
@@ -362,6 +362,24 @@ static int kvm_trap_emul_handle_trap(struct kvm_vcpu *vcpu)
362 return ret; 362 return ret;
363} 363}
364 364
365static int kvm_trap_emul_handle_msa_fpe(struct kvm_vcpu *vcpu)
366{
367 struct kvm_run *run = vcpu->run;
368 uint32_t __user *opc = (uint32_t __user *)vcpu->arch.pc;
369 unsigned long cause = vcpu->arch.host_cp0_cause;
370 enum emulation_result er = EMULATE_DONE;
371 int ret = RESUME_GUEST;
372
373 er = kvm_mips_emulate_msafpe_exc(cause, opc, run, vcpu);
374 if (er == EMULATE_DONE) {
375 ret = RESUME_GUEST;
376 } else {
377 run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
378 ret = RESUME_HOST;
379 }
380 return ret;
381}
382
365static int kvm_trap_emul_handle_fpe(struct kvm_vcpu *vcpu) 383static int kvm_trap_emul_handle_fpe(struct kvm_vcpu *vcpu)
366{ 384{
367 struct kvm_run *run = vcpu->run; 385 struct kvm_run *run = vcpu->run;
@@ -380,16 +398,36 @@ static int kvm_trap_emul_handle_fpe(struct kvm_vcpu *vcpu)
380 return ret; 398 return ret;
381} 399}
382 400
401/**
402 * kvm_trap_emul_handle_msa_disabled() - Guest used MSA while disabled in root.
403 * @vcpu: Virtual CPU context.
404 *
405 * Handle when the guest attempts to use MSA when it is disabled.
406 */
383static int kvm_trap_emul_handle_msa_disabled(struct kvm_vcpu *vcpu) 407static int kvm_trap_emul_handle_msa_disabled(struct kvm_vcpu *vcpu)
384{ 408{
409 struct mips_coproc *cop0 = vcpu->arch.cop0;
385 struct kvm_run *run = vcpu->run; 410 struct kvm_run *run = vcpu->run;
386 uint32_t __user *opc = (uint32_t __user *) vcpu->arch.pc; 411 uint32_t __user *opc = (uint32_t __user *) vcpu->arch.pc;
387 unsigned long cause = vcpu->arch.host_cp0_cause; 412 unsigned long cause = vcpu->arch.host_cp0_cause;
388 enum emulation_result er = EMULATE_DONE; 413 enum emulation_result er = EMULATE_DONE;
389 int ret = RESUME_GUEST; 414 int ret = RESUME_GUEST;
390 415
391 /* No MSA supported in guest, guest reserved instruction exception */ 416 if (!kvm_mips_guest_has_msa(&vcpu->arch) ||
392 er = kvm_mips_emulate_ri_exc(cause, opc, run, vcpu); 417 (kvm_read_c0_guest_status(cop0) & (ST0_CU1 | ST0_FR)) == ST0_CU1) {
418 /*
419 * No MSA in guest, or FPU enabled and not in FR=1 mode,
420 * guest reserved instruction exception
421 */
422 er = kvm_mips_emulate_ri_exc(cause, opc, run, vcpu);
423 } else if (!(kvm_read_c0_guest_config5(cop0) & MIPS_CONF5_MSAEN)) {
424 /* MSA disabled by guest, guest MSA disabled exception */
425 er = kvm_mips_emulate_msadis_exc(cause, opc, run, vcpu);
426 } else {
427 /* Restore MSA/FPU state */
428 kvm_own_msa(vcpu);
429 er = EMULATE_DONE;
430 }
393 431
394 switch (er) { 432 switch (er) {
395 case EMULATE_DONE: 433 case EMULATE_DONE:
@@ -608,6 +646,7 @@ static struct kvm_mips_callbacks kvm_trap_emul_callbacks = {
608 .handle_res_inst = kvm_trap_emul_handle_res_inst, 646 .handle_res_inst = kvm_trap_emul_handle_res_inst,
609 .handle_break = kvm_trap_emul_handle_break, 647 .handle_break = kvm_trap_emul_handle_break,
610 .handle_trap = kvm_trap_emul_handle_trap, 648 .handle_trap = kvm_trap_emul_handle_trap,
649 .handle_msa_fpe = kvm_trap_emul_handle_msa_fpe,
611 .handle_fpe = kvm_trap_emul_handle_fpe, 650 .handle_fpe = kvm_trap_emul_handle_fpe,
612 .handle_msa_disabled = kvm_trap_emul_handle_msa_disabled, 651 .handle_msa_disabled = kvm_trap_emul_handle_msa_disabled,
613 652