Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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

+88 -3
+8
arch/mips/include/asm/kvm_host.h
··· 123 123 u32 resvd_inst_exits; 124 124 u32 break_inst_exits; 125 125 u32 trap_inst_exits; 126 + u32 fpe_exits; 126 127 u32 flush_dcache_exits; 127 128 u32 halt_successful_poll; 128 129 u32 halt_wakeup; ··· 144 143 RESVD_INST_EXITS, 145 144 BREAK_INST_EXITS, 146 145 TRAP_INST_EXITS, 146 + FPE_EXITS, 147 147 FLUSH_DCACHE_EXITS, 148 148 MAX_KVM_MIPS_EXIT_TYPES 149 149 }; ··· 587 585 int (*handle_res_inst)(struct kvm_vcpu *vcpu); 588 586 int (*handle_break)(struct kvm_vcpu *vcpu); 589 587 int (*handle_trap)(struct kvm_vcpu *vcpu); 588 + int (*handle_fpe)(struct kvm_vcpu *vcpu); 590 589 int (*handle_msa_disabled)(struct kvm_vcpu *vcpu); 591 590 int (*vm_init)(struct kvm *kvm); 592 591 int (*vcpu_init)(struct kvm_vcpu *vcpu); ··· 736 733 uint32_t *opc, 737 734 struct kvm_run *run, 738 735 struct kvm_vcpu *vcpu); 736 + 737 + extern enum emulation_result kvm_mips_emulate_fpe_exc(unsigned long cause, 738 + uint32_t *opc, 739 + struct kvm_run *run, 740 + struct kvm_vcpu *vcpu); 739 741 740 742 extern enum emulation_result kvm_mips_complete_mmio_load(struct kvm_vcpu *vcpu, 741 743 struct kvm_run *run);
+36
arch/mips/kvm/emulate.c
··· 2146 2146 return er; 2147 2147 } 2148 2148 2149 + enum 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 2184 /* ll/sc, rdhwr, sync emulation */ 2150 2185 2151 2186 #define OPCODE 0xfc000000 ··· 2388 2353 case T_BREAK: 2389 2354 case T_RES_INST: 2390 2355 case T_TRAP: 2356 + case T_FPE: 2391 2357 case T_MSADIS: 2392 2358 break; 2393 2359
+7
arch/mips/kvm/mips.c
··· 50 50 { "resvd_inst", VCPU_STAT(resvd_inst_exits), KVM_STAT_VCPU }, 51 51 { "break_inst", VCPU_STAT(break_inst_exits), KVM_STAT_VCPU }, 52 52 { "trap_inst", VCPU_STAT(trap_inst_exits), KVM_STAT_VCPU }, 53 + { "fpe", VCPU_STAT(fpe_exits), KVM_STAT_VCPU }, 53 54 { "flush_dcache", VCPU_STAT(flush_dcache_exits), KVM_STAT_VCPU }, 54 55 { "halt_successful_poll", VCPU_STAT(halt_successful_poll), KVM_STAT_VCPU }, 55 56 { "halt_wakeup", VCPU_STAT(halt_wakeup), KVM_STAT_VCPU }, ··· 1147 1146 ++vcpu->stat.trap_inst_exits; 1148 1147 trace_kvm_exit(vcpu, TRAP_INST_EXITS); 1149 1148 ret = kvm_mips_callbacks->handle_trap(vcpu); 1149 + break; 1150 + 1151 + case T_FPE: 1152 + ++vcpu->stat.fpe_exits; 1153 + trace_kvm_exit(vcpu, FPE_EXITS); 1154 + ret = kvm_mips_callbacks->handle_fpe(vcpu); 1150 1155 break; 1151 1156 1152 1157 case T_MSADIS:
+1
arch/mips/kvm/stats.c
··· 26 26 "Reserved Inst", 27 27 "Break Inst", 28 28 "Trap Inst", 29 + "FPE", 29 30 "D-Cache Flushes", 30 31 }; 31 32
+36 -3
arch/mips/kvm/trap_emul.c
··· 39 39 40 40 static int kvm_trap_emul_handle_cop_unusable(struct kvm_vcpu *vcpu) 41 41 { 42 + struct mips_coproc *cop0 = vcpu->arch.cop0; 42 43 struct kvm_run *run = vcpu->run; 43 44 uint32_t __user *opc = (uint32_t __user *) vcpu->arch.pc; 44 45 unsigned long cause = vcpu->arch.host_cp0_cause; 45 46 enum emulation_result er = EMULATE_DONE; 46 47 int ret = RESUME_GUEST; 47 48 48 - if (((cause & CAUSEF_CE) >> CAUSEB_CE) == 1) 49 - er = kvm_mips_emulate_fpu_exc(cause, opc, run, vcpu); 50 - else 49 + if (((cause & CAUSEF_CE) >> CAUSEB_CE) == 1) { 50 + /* FPU Unusable */ 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 64 er = kvm_mips_emulate_inst(cause, opc, run, vcpu); 65 + } 52 66 53 67 switch (er) { 54 68 case EMULATE_DONE: ··· 362 348 return ret; 363 349 } 364 350 351 + static int kvm_trap_emul_handle_fpe(struct kvm_vcpu *vcpu) 352 + { 353 + struct kvm_run *run = vcpu->run; 354 + uint32_t __user *opc = (uint32_t __user *)vcpu->arch.pc; 355 + unsigned long cause = vcpu->arch.host_cp0_cause; 356 + enum emulation_result er = EMULATE_DONE; 357 + int ret = RESUME_GUEST; 358 + 359 + er = kvm_mips_emulate_fpe_exc(cause, opc, run, vcpu); 360 + if (er == EMULATE_DONE) { 361 + ret = RESUME_GUEST; 362 + } else { 363 + run->exit_reason = KVM_EXIT_INTERNAL_ERROR; 364 + ret = RESUME_HOST; 365 + } 366 + return ret; 367 + } 368 + 365 369 static int kvm_trap_emul_handle_msa_disabled(struct kvm_vcpu *vcpu) 366 370 { 367 371 struct kvm_run *run = vcpu->run; ··· 608 576 .handle_res_inst = kvm_trap_emul_handle_res_inst, 609 577 .handle_break = kvm_trap_emul_handle_break, 610 578 .handle_trap = kvm_trap_emul_handle_trap, 579 + .handle_fpe = kvm_trap_emul_handle_fpe, 611 580 .handle_msa_disabled = kvm_trap_emul_handle_msa_disabled, 612 581 613 582 .vm_init = kvm_trap_emul_vm_init,