aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJames Hogan <james.hogan@imgtec.com>2015-02-03 08:59:38 -0500
committerJames Hogan <james.hogan@imgtec.com>2015-03-27 17:25:15 -0400
commit6cdc65e31d4f70561d71eeaf34a2a70ab68bf146 (patch)
treeaa584b920b260d7d808ec4d80ea7bc6b2b74e150
parent98e91b8457d81f53fab990fac6c57e2a43c47627 (diff)
MIPS: KVM: Emulate FPU bits in COP0 interface
Emulate FPU related parts of COP0 interface so that the guest will be able to enable/disable the following once the FPU capability has been wired up: - The FPU (Status.CU1) - 64-bit FP register mode (Status.FR) - Hybrid FP register mode (Config5.FRE) Changing Status.CU1 has no immediate effect if the FPU state isn't live, as the FPU state is restored lazily on first use. After that, changes take place immediately in the host Status.CU1, so that the guest can start getting coprocessor unusable exceptions right away for guest FPU operations if it is disabled. The FPU state is saved lazily too, as the FPU may get re-enabled in the near future anyway. Any change to Status.FR causes the FPU state to be discarded and FPU disabled, as the register state is architecturally UNPREDICTABLE after such a change. This should also ensure that the FPU state is fully initialised (with stale state, but that's fine) when it is next used in the new FP mode. Any change to the Config5.FRE bit is immediately updated in the host state so that the guest can get the relevant exceptions right away for single-precision FPU operations. 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/kvm/emulate.c111
1 files changed, 100 insertions, 11 deletions
diff --git a/arch/mips/kvm/emulate.c b/arch/mips/kvm/emulate.c
index 91d5b0e370b4..3511bb20fe0e 100644
--- a/arch/mips/kvm/emulate.c
+++ b/arch/mips/kvm/emulate.c
@@ -893,8 +893,13 @@ enum emulation_result kvm_mips_emul_tlbp(struct kvm_vcpu *vcpu)
893 */ 893 */
894unsigned int kvm_mips_config1_wrmask(struct kvm_vcpu *vcpu) 894unsigned int kvm_mips_config1_wrmask(struct kvm_vcpu *vcpu)
895{ 895{
896 /* Read-only */ 896 unsigned int mask = 0;
897 return 0; 897
898 /* Permit FPU to be present if FPU is supported */
899 if (kvm_mips_guest_can_have_fpu(&vcpu->arch))
900 mask |= MIPS_CONF1_FP;
901
902 return mask;
898} 903}
899 904
900/** 905/**
@@ -932,8 +937,19 @@ unsigned int kvm_mips_config4_wrmask(struct kvm_vcpu *vcpu)
932 */ 937 */
933unsigned int kvm_mips_config5_wrmask(struct kvm_vcpu *vcpu) 938unsigned int kvm_mips_config5_wrmask(struct kvm_vcpu *vcpu)
934{ 939{
935 /* Read-only */ 940 unsigned int mask = 0;
936 return 0; 941
942 /*
943 * Permit guest FPU mode changes if FPU is enabled and the relevant
944 * feature exists according to FIR register.
945 */
946 if (kvm_mips_guest_has_fpu(&vcpu->arch)) {
947 if (cpu_has_fre)
948 mask |= MIPS_CONF5_FRE;
949 /* We don't support UFR or UFE */
950 }
951
952 return mask;
937} 953}
938 954
939enum emulation_result kvm_mips_emulate_CP0(uint32_t inst, uint32_t *opc, 955enum emulation_result kvm_mips_emulate_CP0(uint32_t inst, uint32_t *opc,
@@ -1073,18 +1089,91 @@ enum emulation_result kvm_mips_emulate_CP0(uint32_t inst, uint32_t *opc,
1073 kvm_mips_write_compare(vcpu, 1089 kvm_mips_write_compare(vcpu,
1074 vcpu->arch.gprs[rt]); 1090 vcpu->arch.gprs[rt]);
1075 } else if ((rd == MIPS_CP0_STATUS) && (sel == 0)) { 1091 } else if ((rd == MIPS_CP0_STATUS) && (sel == 0)) {
1076 kvm_write_c0_guest_status(cop0, 1092 unsigned int old_val, val, change;
1077 vcpu->arch.gprs[rt]); 1093
1094 old_val = kvm_read_c0_guest_status(cop0);
1095 val = vcpu->arch.gprs[rt];
1096 change = val ^ old_val;
1097
1098 /* Make sure that the NMI bit is never set */
1099 val &= ~ST0_NMI;
1100
1101 /*
1102 * Don't allow CU1 or FR to be set unless FPU
1103 * capability enabled and exists in guest
1104 * configuration.
1105 */
1106 if (!kvm_mips_guest_has_fpu(&vcpu->arch))
1107 val &= ~(ST0_CU1 | ST0_FR);
1108
1109 /*
1110 * Also don't allow FR to be set if host doesn't
1111 * support it.
1112 */
1113 if (!(current_cpu_data.fpu_id & MIPS_FPIR_F64))
1114 val &= ~ST0_FR;
1115
1116
1117 /* Handle changes in FPU mode */
1118 preempt_disable();
1119
1078 /* 1120 /*
1079 * Make sure that CU1 and NMI bits are 1121 * FPU and Vector register state is made
1080 * never set 1122 * UNPREDICTABLE by a change of FR, so don't
1123 * even bother saving it.
1081 */ 1124 */
1082 kvm_clear_c0_guest_status(cop0, 1125 if (change & ST0_FR)
1083 (ST0_CU1 | ST0_NMI)); 1126 kvm_drop_fpu(vcpu);
1127
1128 /*
1129 * Propagate CU1 (FPU enable) changes
1130 * immediately if the FPU context is already
1131 * loaded. When disabling we leave the context
1132 * loaded so it can be quickly enabled again in
1133 * the near future.
1134 */
1135 if (change & ST0_CU1 &&
1136 vcpu->arch.fpu_inuse & KVM_MIPS_FPU_FPU)
1137 change_c0_status(ST0_CU1, val);
1138
1139 preempt_enable();
1140
1141 kvm_write_c0_guest_status(cop0, val);
1084 1142
1085#ifdef CONFIG_KVM_MIPS_DYN_TRANS 1143#ifdef CONFIG_KVM_MIPS_DYN_TRANS
1086 kvm_mips_trans_mtc0(inst, opc, vcpu); 1144 /*
1145 * If FPU present, we need CU1/FR bits to take
1146 * effect fairly soon.
1147 */
1148 if (!kvm_mips_guest_has_fpu(&vcpu->arch))
1149 kvm_mips_trans_mtc0(inst, opc, vcpu);
1087#endif 1150#endif
1151 } else if ((rd == MIPS_CP0_CONFIG) && (sel == 5)) {
1152 unsigned int old_val, val, change, wrmask;
1153
1154 old_val = kvm_read_c0_guest_config5(cop0);
1155 val = vcpu->arch.gprs[rt];
1156
1157 /* Only a few bits are writable in Config5 */
1158 wrmask = kvm_mips_config5_wrmask(vcpu);
1159 change = (val ^ old_val) & wrmask;
1160 val = old_val ^ change;
1161
1162
1163 /* Handle changes in FPU modes */
1164 preempt_disable();
1165
1166 /*
1167 * Propagate FRE changes immediately if the FPU
1168 * context is already loaded.
1169 */
1170 if (change & MIPS_CONF5_FRE &&
1171 vcpu->arch.fpu_inuse & KVM_MIPS_FPU_FPU)
1172 change_c0_config5(MIPS_CONF5_FRE, val);
1173
1174 preempt_enable();
1175
1176 kvm_write_c0_guest_config5(cop0, val);
1088 } else if ((rd == MIPS_CP0_CAUSE) && (sel == 0)) { 1177 } else if ((rd == MIPS_CP0_CAUSE) && (sel == 0)) {
1089 uint32_t old_cause, new_cause; 1178 uint32_t old_cause, new_cause;
1090 1179