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:16 -0400
commit1c0cd66adbac8aa339b9521eceb18b00d1b0699e (patch)
tree62a893856435ad26ac8724cb23c3e47b63d177bd
parent6cdc65e31d4f70561d71eeaf34a2a70ab68bf146 (diff)
MIPS: KVM: Add FP exception handling
Add guest exception handling for floating point exceptions and coprocessor 1 unusable exceptions. Floating point exceptions from the guest need passing to the guest kernel, so for these a guest FPE is emulated. Also, coprocessor 1 unusable exceptions are normally passed straight through to the guest (because no guest FPU was supported), but the hypervisor can now handle them if the guest has its FPU enabled by restoring the guest FPU context and enabling 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.h8
-rw-r--r--arch/mips/kvm/emulate.c36
-rw-r--r--arch/mips/kvm/mips.c7
-rw-r--r--arch/mips/kvm/stats.c1
-rw-r--r--arch/mips/kvm/trap_emul.c39
5 files changed, 88 insertions, 3 deletions
diff --git a/arch/mips/include/asm/kvm_host.h b/arch/mips/include/asm/kvm_host.h
index 866edf330e53..fb264d8695e4 100644
--- a/arch/mips/include/asm/kvm_host.h
+++ b/arch/mips/include/asm/kvm_host.h
@@ -123,6 +123,7 @@ 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 fpe_exits;
126 u32 flush_dcache_exits; 127 u32 flush_dcache_exits;
127 u32 halt_successful_poll; 128 u32 halt_successful_poll;
128 u32 halt_wakeup; 129 u32 halt_wakeup;
@@ -143,6 +144,7 @@ enum kvm_mips_exit_types {
143 RESVD_INST_EXITS, 144 RESVD_INST_EXITS,
144 BREAK_INST_EXITS, 145 BREAK_INST_EXITS,
145 TRAP_INST_EXITS, 146 TRAP_INST_EXITS,
147 FPE_EXITS,
146 FLUSH_DCACHE_EXITS, 148 FLUSH_DCACHE_EXITS,
147 MAX_KVM_MIPS_EXIT_TYPES 149 MAX_KVM_MIPS_EXIT_TYPES
148}; 150};
@@ -585,6 +587,7 @@ struct kvm_mips_callbacks {
585 int (*handle_res_inst)(struct kvm_vcpu *vcpu); 587 int (*handle_res_inst)(struct kvm_vcpu *vcpu);
586 int (*handle_break)(struct kvm_vcpu *vcpu); 588 int (*handle_break)(struct kvm_vcpu *vcpu);
587 int (*handle_trap)(struct kvm_vcpu *vcpu); 589 int (*handle_trap)(struct kvm_vcpu *vcpu);
590 int (*handle_fpe)(struct kvm_vcpu *vcpu);
588 int (*handle_msa_disabled)(struct kvm_vcpu *vcpu); 591 int (*handle_msa_disabled)(struct kvm_vcpu *vcpu);
589 int (*vm_init)(struct kvm *kvm); 592 int (*vm_init)(struct kvm *kvm);
590 int (*vcpu_init)(struct kvm_vcpu *vcpu); 593 int (*vcpu_init)(struct kvm_vcpu *vcpu);
@@ -734,6 +737,11 @@ extern enum emulation_result kvm_mips_emulate_trap_exc(unsigned long cause,
734 struct kvm_run *run, 737 struct kvm_run *run,
735 struct kvm_vcpu *vcpu); 738 struct kvm_vcpu *vcpu);
736 739
740extern enum emulation_result kvm_mips_emulate_fpe_exc(unsigned long cause,
741 uint32_t *opc,
742 struct kvm_run *run,
743 struct kvm_vcpu *vcpu);
744
737extern enum emulation_result kvm_mips_complete_mmio_load(struct kvm_vcpu *vcpu, 745extern enum emulation_result kvm_mips_complete_mmio_load(struct kvm_vcpu *vcpu,
738 struct kvm_run *run); 746 struct kvm_run *run);
739 747
diff --git a/arch/mips/kvm/emulate.c b/arch/mips/kvm/emulate.c
index 3511bb20fe0e..fbf169fb63df 100644
--- a/arch/mips/kvm/emulate.c
+++ b/arch/mips/kvm/emulate.c
@@ -2146,6 +2146,41 @@ enum emulation_result kvm_mips_emulate_trap_exc(unsigned long cause,
2146 return er; 2146 return er;
2147} 2147}
2148 2148
2149enum emulation_result kvm_mips_emulate_fpe_exc(unsigned long cause,
2150 uint32_t *opc,
2151 struct kvm_run *run,
2152 struct kvm_vcpu *vcpu)
2153{
2154 struct mips_coproc *cop0 = vcpu->arch.cop0;
2155 struct kvm_vcpu_arch *arch = &vcpu->arch;
2156 enum emulation_result er = EMULATE_DONE;
2157
2158 if ((kvm_read_c0_guest_status(cop0) & ST0_EXL) == 0) {
2159 /* save old pc */
2160 kvm_write_c0_guest_epc(cop0, arch->pc);
2161 kvm_set_c0_guest_status(cop0, ST0_EXL);
2162
2163 if (cause & CAUSEF_BD)
2164 kvm_set_c0_guest_cause(cop0, CAUSEF_BD);
2165 else
2166 kvm_clear_c0_guest_cause(cop0, CAUSEF_BD);
2167
2168 kvm_debug("Delivering FPE @ pc %#lx\n", arch->pc);
2169
2170 kvm_change_c0_guest_cause(cop0, (0xff),
2171 (T_FPE << CAUSEB_EXCCODE));
2172
2173 /* Set PC to the exception entry point */
2174 arch->pc = KVM_GUEST_KSEG0 + 0x180;
2175
2176 } else {
2177 kvm_err("Trying to deliver FPE when EXL is already set\n");
2178 er = EMULATE_FAIL;
2179 }
2180
2181 return er;
2182}
2183
2149/* ll/sc, rdhwr, sync emulation */ 2184/* ll/sc, rdhwr, sync emulation */
2150 2185
2151#define OPCODE 0xfc000000 2186#define OPCODE 0xfc000000
@@ -2353,6 +2388,7 @@ enum emulation_result kvm_mips_check_privilege(unsigned long cause,
2353 case T_BREAK: 2388 case T_BREAK:
2354 case T_RES_INST: 2389 case T_RES_INST:
2355 case T_TRAP: 2390 case T_TRAP:
2391 case T_FPE:
2356 case T_MSADIS: 2392 case T_MSADIS:
2357 break; 2393 break;
2358 2394
diff --git a/arch/mips/kvm/mips.c b/arch/mips/kvm/mips.c
index b26a48d81467..dd0833833bea 100644
--- a/arch/mips/kvm/mips.c
+++ b/arch/mips/kvm/mips.c
@@ -50,6 +50,7 @@ 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 { "fpe", VCPU_STAT(fpe_exits), KVM_STAT_VCPU },
53 { "flush_dcache", VCPU_STAT(flush_dcache_exits), KVM_STAT_VCPU }, 54 { "flush_dcache", VCPU_STAT(flush_dcache_exits), KVM_STAT_VCPU },
54 { "halt_successful_poll", VCPU_STAT(halt_successful_poll), KVM_STAT_VCPU }, 55 { "halt_successful_poll", VCPU_STAT(halt_successful_poll), KVM_STAT_VCPU },
55 { "halt_wakeup", VCPU_STAT(halt_wakeup), KVM_STAT_VCPU }, 56 { "halt_wakeup", VCPU_STAT(halt_wakeup), KVM_STAT_VCPU },
@@ -1148,6 +1149,12 @@ int kvm_mips_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu)
1148 ret = kvm_mips_callbacks->handle_trap(vcpu); 1149 ret = kvm_mips_callbacks->handle_trap(vcpu);
1149 break; 1150 break;
1150 1151
1152 case T_FPE:
1153 ++vcpu->stat.fpe_exits;
1154 trace_kvm_exit(vcpu, FPE_EXITS);
1155 ret = kvm_mips_callbacks->handle_fpe(vcpu);
1156 break;
1157
1151 case T_MSADIS: 1158 case T_MSADIS:
1152 ret = kvm_mips_callbacks->handle_msa_disabled(vcpu); 1159 ret = kvm_mips_callbacks->handle_msa_disabled(vcpu);
1153 break; 1160 break;
diff --git a/arch/mips/kvm/stats.c b/arch/mips/kvm/stats.c
index dd90b0a92181..3843828f3b91 100644
--- a/arch/mips/kvm/stats.c
+++ b/arch/mips/kvm/stats.c
@@ -26,6 +26,7 @@ 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 "FPE",
29 "D-Cache Flushes", 30 "D-Cache Flushes",
30}; 31};
31 32
diff --git a/arch/mips/kvm/trap_emul.c b/arch/mips/kvm/trap_emul.c
index 408af244aed2..421d5b815f24 100644
--- a/arch/mips/kvm/trap_emul.c
+++ b/arch/mips/kvm/trap_emul.c
@@ -39,16 +39,30 @@ static gpa_t kvm_trap_emul_gva_to_gpa_cb(gva_t gva)
39 39
40static int kvm_trap_emul_handle_cop_unusable(struct kvm_vcpu *vcpu) 40static int kvm_trap_emul_handle_cop_unusable(struct kvm_vcpu *vcpu)
41{ 41{
42 struct mips_coproc *cop0 = vcpu->arch.cop0;
42 struct kvm_run *run = vcpu->run; 43 struct kvm_run *run = vcpu->run;
43 uint32_t __user *opc = (uint32_t __user *) vcpu->arch.pc; 44 uint32_t __user *opc = (uint32_t __user *) vcpu->arch.pc;
44 unsigned long cause = vcpu->arch.host_cp0_cause; 45 unsigned long cause = vcpu->arch.host_cp0_cause;
45 enum emulation_result er = EMULATE_DONE; 46 enum emulation_result er = EMULATE_DONE;
46 int ret = RESUME_GUEST; 47 int ret = RESUME_GUEST;
47 48
48 if (((cause & CAUSEF_CE) >> CAUSEB_CE) == 1) 49 if (((cause & CAUSEF_CE) >> CAUSEB_CE) == 1) {
49 er = kvm_mips_emulate_fpu_exc(cause, opc, run, vcpu); 50 /* FPU Unusable */
50 else 51 if (!kvm_mips_guest_has_fpu(&vcpu->arch) ||
52 (kvm_read_c0_guest_status(cop0) & ST0_CU1) == 0) {
53 /*
54 * Unusable/no FPU in guest:
55 * deliver guest COP1 Unusable Exception
56 */
57 er = kvm_mips_emulate_fpu_exc(cause, opc, run, vcpu);
58 } else {
59 /* Restore FPU state */
60 kvm_own_fpu(vcpu);
61 er = EMULATE_DONE;
62 }
63 } else {
51 er = kvm_mips_emulate_inst(cause, opc, run, vcpu); 64 er = kvm_mips_emulate_inst(cause, opc, run, vcpu);
65 }
52 66
53 switch (er) { 67 switch (er) {
54 case EMULATE_DONE: 68 case EMULATE_DONE:
@@ -348,6 +362,24 @@ static int kvm_trap_emul_handle_trap(struct kvm_vcpu *vcpu)
348 return ret; 362 return ret;
349} 363}
350 364
365static int kvm_trap_emul_handle_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_fpe_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
351static int kvm_trap_emul_handle_msa_disabled(struct kvm_vcpu *vcpu) 383static int kvm_trap_emul_handle_msa_disabled(struct kvm_vcpu *vcpu)
352{ 384{
353 struct kvm_run *run = vcpu->run; 385 struct kvm_run *run = vcpu->run;
@@ -576,6 +608,7 @@ static struct kvm_mips_callbacks kvm_trap_emul_callbacks = {
576 .handle_res_inst = kvm_trap_emul_handle_res_inst, 608 .handle_res_inst = kvm_trap_emul_handle_res_inst,
577 .handle_break = kvm_trap_emul_handle_break, 609 .handle_break = kvm_trap_emul_handle_break,
578 .handle_trap = kvm_trap_emul_handle_trap, 610 .handle_trap = kvm_trap_emul_handle_trap,
611 .handle_fpe = kvm_trap_emul_handle_fpe,
579 .handle_msa_disabled = kvm_trap_emul_handle_msa_disabled, 612 .handle_msa_disabled = kvm_trap_emul_handle_msa_disabled,
580 613
581 .vm_init = kvm_trap_emul_vm_init, 614 .vm_init = kvm_trap_emul_vm_init,