]> www.infradead.org Git - linux-platform-drivers-x86.git/commitdiff
KVM: MIPS: Add CPUCFG emulation for Loongson-3
authorHuacai Chen <chenhc@lemote.com>
Sat, 23 May 2020 07:56:38 +0000 (15:56 +0800)
committerPaolo Bonzini <pbonzini@redhat.com>
Thu, 4 Jun 2020 17:51:33 +0000 (13:51 -0400)
Loongson-3 overrides lwc2 instructions to implement CPUCFG and CSR
read/write functions. These instructions all cause guest exit so CSR
doesn't benifit KVM guest (and there are always legacy methods to
provide the same functions as CSR). So, we only emulate CPUCFG and let
it return a reduced feature list (which means the virtual CPU doesn't
have any other advanced features, including CSR) in KVM.

Reviewed-by: Aleksandar Markovic <aleksandar.qemu.devel@gmail.com>
Signed-off-by: Huacai Chen <chenhc@lemote.com>
Co-developed-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
Message-Id: <1590220602-3547-12-git-send-email-chenhc@lemote.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
arch/mips/include/asm/kvm_host.h
arch/mips/include/uapi/asm/inst.h
arch/mips/kvm/mips.c
arch/mips/kvm/vz.c

index c4e6b1e954f5f2cb68cdbf5d43faa546d8d1e438..9f3bfc8b363de16e53be0ac5f3a304e09d76b9ed 100644 (file)
@@ -173,6 +173,9 @@ struct kvm_vcpu_stat {
        u64 vz_ghfc_exits;
        u64 vz_gpa_exits;
        u64 vz_resvd_exits;
+#ifdef CONFIG_CPU_LOONGSON64
+       u64 vz_cpucfg_exits;
+#endif
 #endif
        u64 halt_successful_poll;
        u64 halt_attempted_poll;
index 98f97c85e0594d8336255185c4f2415ae9cef54b..43d1faa02933c8428eb9f2c48c6cb62d28ffeb68 100644 (file)
@@ -1012,6 +1012,16 @@ struct loongson3_lsdc2_format {  /* Loongson-3 overridden ldc2/sdc2 Load/Store fo
        ;))))))
 };
 
+struct loongson3_lscsr_format {        /* Loongson-3 CPUCFG&CSR read/write format */
+       __BITFIELD_FIELD(unsigned int opcode : 6,
+       __BITFIELD_FIELD(unsigned int rs : 5,
+       __BITFIELD_FIELD(unsigned int fr : 5,
+       __BITFIELD_FIELD(unsigned int rd : 5,
+       __BITFIELD_FIELD(unsigned int fd : 5,
+       __BITFIELD_FIELD(unsigned int func : 6,
+       ;))))))
+};
+
 /*
  * MIPS16e instruction formats (16-bit length)
  */
@@ -1114,6 +1124,7 @@ union mips_instruction {
        struct mm16_r5_format mm16_r5_format;
        struct loongson3_lswc2_format loongson3_lswc2_format;
        struct loongson3_lsdc2_format loongson3_lsdc2_format;
+       struct loongson3_lscsr_format loongson3_lscsr_format;
 };
 
 union mips16e_instruction {
index e4d42e03ec37d932d89bb245afdb75644fd561bc..f62667bb54f36f443f959c0050a2891e06918e0e 100644 (file)
@@ -67,6 +67,7 @@ struct kvm_stats_debugfs_item debugfs_entries[] = {
        VCPU_STAT("vz_ghfc", vz_ghfc_exits),
        VCPU_STAT("vz_gpa", vz_gpa_exits),
        VCPU_STAT("vz_resvd", vz_resvd_exits),
+       VCPU_STAT("vz_cpucfg", vz_cpucfg_exits),
 #endif
        VCPU_STAT("halt_successful_poll", halt_successful_poll),
        VCPU_STAT("halt_attempted_poll", halt_attempted_poll),
index 45be0b7bc09048837fee2e0f140795a223a872a0..c09e8f13b256403f9832c419f6ae53959dfa99cc 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)
 {
@@ -1120,6 +1192,11 @@ static enum emulation_result kvm_trap_vz_handle_gpsi(u32 cause, u32 *opc,
                trace_kvm_exit(vcpu, KVM_TRACE_EXIT_CACHE);
                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) {