KVM: MIPS: Add CPUCFG emulation for Loongson-3
[platform/kernel/linux-starfive.git] / arch / mips / kvm / vz.c
index 45be0b7..c09e8f1 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/kvm_host.h>
 
 #include "interrupt.h"
+#include "loongson_regs.h"
 
 #include "trace.h"
 
@@ -1092,6 +1093,77 @@ static enum emulation_result kvm_vz_gpsi_cache(union mips_instruction inst,
        return EMULATE_FAIL;
 }
 
+#ifdef CONFIG_CPU_LOONGSON64
+static enum emulation_result kvm_vz_gpsi_lwc2(union mips_instruction inst,
+                                             u32 *opc, u32 cause,
+                                             struct kvm_run *run,
+                                             struct kvm_vcpu *vcpu)
+{
+       unsigned int rs, rd;
+       unsigned int hostcfg;
+       unsigned long curr_pc;
+       enum emulation_result er = EMULATE_DONE;
+
+       /*
+        * Update PC and hold onto current PC in case there is
+        * an error and we want to rollback the PC
+        */
+       curr_pc = vcpu->arch.pc;
+       er = update_pc(vcpu, cause);
+       if (er == EMULATE_FAIL)
+               return er;
+
+       rs = inst.loongson3_lscsr_format.rs;
+       rd = inst.loongson3_lscsr_format.rd;
+       switch (inst.loongson3_lscsr_format.fr) {
+       case 0x8:  /* Read CPUCFG */
+               ++vcpu->stat.vz_cpucfg_exits;
+               hostcfg = read_cpucfg(vcpu->arch.gprs[rs]);
+
+               switch (vcpu->arch.gprs[rs]) {
+               case LOONGSON_CFG0:
+                       vcpu->arch.gprs[rd] = 0x14c000;
+                       break;
+               case LOONGSON_CFG1:
+                       hostcfg &= (LOONGSON_CFG1_FP | LOONGSON_CFG1_MMI |
+                                   LOONGSON_CFG1_MSA1 | LOONGSON_CFG1_MSA2 |
+                                   LOONGSON_CFG1_SFBP);
+                       vcpu->arch.gprs[rd] = hostcfg;
+                       break;
+               case LOONGSON_CFG2:
+                       hostcfg &= (LOONGSON_CFG2_LEXT1 | LOONGSON_CFG2_LEXT2 |
+                                   LOONGSON_CFG2_LEXT3 | LOONGSON_CFG2_LSPW);
+                       vcpu->arch.gprs[rd] = hostcfg;
+                       break;
+               case LOONGSON_CFG3:
+                       vcpu->arch.gprs[rd] = hostcfg;
+                       break;
+               default:
+                       /* Don't export any other advanced features to guest */
+                       vcpu->arch.gprs[rd] = 0;
+                       break;
+               }
+               break;
+
+       default:
+               kvm_err("lwc2 emulate not impl %d rs %lx @%lx\n",
+                       inst.loongson3_lscsr_format.fr, vcpu->arch.gprs[rs], curr_pc);
+               er = EMULATE_FAIL;
+               break;
+       }
+
+       /* Rollback PC only if emulation was unsuccessful */
+       if (er == EMULATE_FAIL) {
+               kvm_err("[%#lx]%s: unsupported lwc2 instruction 0x%08x 0x%08x\n",
+                       curr_pc, __func__, inst.word, inst.loongson3_lscsr_format.fr);
+
+               vcpu->arch.pc = curr_pc;
+       }
+
+       return er;
+}
+#endif
+
 static enum emulation_result kvm_trap_vz_handle_gpsi(u32 cause, u32 *opc,
                                                     struct kvm_vcpu *vcpu)
 {
@@ -1121,6 +1193,11 @@ static enum emulation_result kvm_trap_vz_handle_gpsi(u32 cause, u32 *opc,
                er = kvm_vz_gpsi_cache(inst, opc, cause, run, vcpu);
                break;
 #endif
+#ifdef CONFIG_CPU_LOONGSON64
+       case lwc2_op:
+               er = kvm_vz_gpsi_lwc2(inst, opc, cause, run, vcpu);
+               break;
+#endif
        case spec3_op:
                switch (inst.spec3_format.func) {
 #ifdef CONFIG_CPU_MIPSR6