This patch add more MMIO load/store instructions emulation, which can
be observed in QXL and some other device drivers:

1, LWL, LWR, LDW, LDR, SWL, SWR, SDL and SDR for all MIPS;
2, GSLBX, GSLHX, GSLWX, GSLDX, GSSBX, GSSHX, GSSWX and GSSDX for
   Loongson-3.

Signed-off-by: Huacai Chen <che...@lemote.com>
Co-developed-by: Jiaxun Yang <jiaxun.y...@flygoat.com>
---
 arch/mips/kvm/emulate.c | 480 +++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 470 insertions(+), 10 deletions(-)

diff --git a/arch/mips/kvm/emulate.c b/arch/mips/kvm/emulate.c
index 3946499..71316fa 100644
--- a/arch/mips/kvm/emulate.c
+++ b/arch/mips/kvm/emulate.c
@@ -1604,6 +1604,7 @@ enum emulation_result kvm_mips_emulate_store(union 
mips_instruction inst,
        enum emulation_result er;
        u32 rt;
        void *data = run->mmio.data;
+       unsigned int imme;
        unsigned long curr_pc;
 
        /*
@@ -1661,6 +1662,211 @@ enum emulation_result kvm_mips_emulate_store(union 
mips_instruction inst,
                          vcpu->arch.gprs[rt], *(u8 *)data);
                break;
 
+       case swl_op:
+               run->mmio.phys_addr = kvm_mips_callbacks->gva_to_gpa(
+                                       vcpu->arch.host_cp0_badvaddr) & (~0x3);
+               run->mmio.len = 4;
+               imme = vcpu->arch.host_cp0_badvaddr & 0x3;
+               switch (imme) {
+               case 0:
+                       *(u32 *)data = ((*(u32 *)data) & 0xffffff00) |
+                                       (vcpu->arch.gprs[rt] >> 24);
+                       break;
+               case 1:
+                       *(u32 *)data = ((*(u32 *)data) & 0xffff0000) |
+                                       (vcpu->arch.gprs[rt] >> 16);
+                       break;
+               case 2:
+                       *(u32 *)data = ((*(u32 *)data) & 0xff000000) |
+                                       (vcpu->arch.gprs[rt] >> 8);
+                       break;
+               case 3:
+                       *(u32 *)data = vcpu->arch.gprs[rt];
+                       break;
+               default:
+                       break;
+               }
+
+               kvm_debug("[%#lx] OP_SWL: eaddr: %#lx, gpr: %#lx, data: %#x\n",
+                         vcpu->arch.pc, vcpu->arch.host_cp0_badvaddr,
+                         vcpu->arch.gprs[rt], *(u32 *)data);
+               break;
+
+       case swr_op:
+               run->mmio.phys_addr = kvm_mips_callbacks->gva_to_gpa(
+                                       vcpu->arch.host_cp0_badvaddr) & (~0x3);
+               run->mmio.len = 4;
+               imme = vcpu->arch.host_cp0_badvaddr & 0x3;
+               switch (imme) {
+               case 0:
+                       *(u32 *)data = vcpu->arch.gprs[rt];
+                       break;
+               case 1:
+                       *(u32 *)data = ((*(u32 *)data) & 0xff) |
+                                       (vcpu->arch.gprs[rt] << 8);
+                       break;
+               case 2:
+                       *(u32 *)data = ((*(u32 *)data) & 0xffff) |
+                                       (vcpu->arch.gprs[rt] << 16);
+                       break;
+               case 3:
+                       *(u32 *)data = ((*(u32 *)data) & 0xffffff) |
+                                       (vcpu->arch.gprs[rt] << 24);
+                       break;
+               default:
+                       break;
+               }
+
+               kvm_debug("[%#lx] OP_SWR: eaddr: %#lx, gpr: %#lx, data: %#x\n",
+                         vcpu->arch.pc, vcpu->arch.host_cp0_badvaddr,
+                         vcpu->arch.gprs[rt], *(u32 *)data);
+               break;
+
+       case sdl_op:
+               run->mmio.phys_addr = kvm_mips_callbacks->gva_to_gpa(
+                                       vcpu->arch.host_cp0_badvaddr) & (~0x7);
+
+               run->mmio.len = 8;
+               imme = vcpu->arch.host_cp0_badvaddr & 0x7;
+               switch (imme) {
+               case 0:
+                       *(u64 *)data = ((*(u64 *)data) & 0xffffffffffffff00) |
+                                       ((vcpu->arch.gprs[rt] >> 56) & 0xff);
+                       break;
+               case 1:
+                       *(u64 *)data = ((*(u64 *)data) & 0xffffffffffff0000) |
+                                       ((vcpu->arch.gprs[rt] >> 48) & 0xffff);
+                       break;
+               case 2:
+                       *(u64 *)data = ((*(u64 *)data) & 0xffffffffff000000) |
+                                       ((vcpu->arch.gprs[rt] >> 40) & 
0xffffff);
+                       break;
+               case 3:
+                       *(u64 *)data = ((*(u64 *)data) & 0xffffffff00000000) |
+                                       ((vcpu->arch.gprs[rt] >> 32) & 
0xffffffff);
+                       break;
+               case 4:
+                       *(u64 *)data = ((*(u64 *)data) & 0xffffff0000000000) |
+                                       ((vcpu->arch.gprs[rt] >> 24) & 
0xffffffffff);
+                       break;
+               case 5:
+                       *(u64 *)data = ((*(u64 *)data) & 0xffff000000000000) |
+                                       ((vcpu->arch.gprs[rt] >> 16) & 
0xffffffffffff);
+                       break;
+               case 6:
+                       *(u64 *)data = ((*(u64 *)data) & 0xff00000000000000) |
+                                       ((vcpu->arch.gprs[rt] >> 8) & 
0xffffffffffffff);
+                       break;
+               case 7:
+                       *(u64 *)data = vcpu->arch.gprs[rt];
+                       break;
+               default:
+                       break;
+               }
+
+               kvm_debug("[%#lx] OP_SDL: eaddr: %#lx, gpr: %#lx, data: %llx\n",
+                         vcpu->arch.pc, vcpu->arch.host_cp0_badvaddr,
+                         vcpu->arch.gprs[rt], *(u64 *)data);
+               break;
+
+       case sdr_op:
+               run->mmio.phys_addr = kvm_mips_callbacks->gva_to_gpa(
+                                       vcpu->arch.host_cp0_badvaddr) & (~0x7);
+
+               run->mmio.len = 8;
+               imme = vcpu->arch.host_cp0_badvaddr & 0x7;
+               switch (imme) {
+               case 0:
+                       *(u64 *)data = vcpu->arch.gprs[rt];
+                       break;
+               case 1:
+                       *(u64 *)data = ((*(u64 *)data) & 0xff) |
+                                       (vcpu->arch.gprs[rt] << 8);
+                       break;
+               case 2:
+                       *(u64 *)data = ((*(u64 *)data) & 0xffff) |
+                                       (vcpu->arch.gprs[rt] << 16);
+                       break;
+               case 3:
+                       *(u64 *)data = ((*(u64 *)data) & 0xffffff) |
+                                       (vcpu->arch.gprs[rt] << 24);
+                       break;
+               case 4:
+                       *(u64 *)data = ((*(u64 *)data) & 0xffffffff) |
+                                       (vcpu->arch.gprs[rt] << 32);
+                       break;
+               case 5:
+                       *(u64 *)data = ((*(u64 *)data) & 0xffffffffff) |
+                                       (vcpu->arch.gprs[rt] << 40);
+                       break;
+               case 6:
+                       *(u64 *)data = ((*(u64 *)data) & 0xffffffffffff) |
+                                       (vcpu->arch.gprs[rt] << 48);
+                       break;
+               case 7:
+                       *(u64 *)data = ((*(u64 *)data) & 0xffffffffffffff) |
+                                       (vcpu->arch.gprs[rt] << 56);
+                       break;
+               default:
+                       break;
+               }
+
+               kvm_debug("[%#lx] OP_SDR: eaddr: %#lx, gpr: %#lx, data: %llx\n",
+                         vcpu->arch.pc, vcpu->arch.host_cp0_badvaddr,
+                         vcpu->arch.gprs[rt], *(u64 *)data);
+               break;
+
+#ifdef CONFIG_CPU_LOONGSON64
+       case sdc2_op:
+               rt = inst.loongson3_lsdc2_format.rt;
+               switch (inst.loongson3_lsdc2_format.opcode1) {
+               /*
+                * Loongson-3 overridden sdc2 instructions.
+                * opcode1              instruction
+                *   0x0          gssbx: store 1 bytes from GPR
+                *   0x1          gsshx: store 2 bytes from GPR
+                *   0x2          gsswx: store 4 bytes from GPR
+                *   0x3          gssdx: store 8 bytes from GPR
+                */
+               case 0x0:
+                       run->mmio.len = 1;
+                       *(u8 *)data = vcpu->arch.gprs[rt];
+
+                       kvm_debug("[%#lx] OP_GSSBX: eaddr: %#lx, gpr: %#lx, 
data: %#x\n",
+                                 vcpu->arch.pc, vcpu->arch.host_cp0_badvaddr,
+                                 vcpu->arch.gprs[rt], *(u8 *)data);
+                       break;
+               case 0x1:
+                       run->mmio.len = 2;
+                       *(u16 *)data = vcpu->arch.gprs[rt];
+
+                       kvm_debug("[%#lx] OP_GSSSHX: eaddr: %#lx, gpr: %#lx, 
data: %#x\n",
+                                 vcpu->arch.pc, vcpu->arch.host_cp0_badvaddr,
+                                 vcpu->arch.gprs[rt], *(u16 *)data);
+                       break;
+               case 0x2:
+                       run->mmio.len = 4;
+                       *(u32 *)data = vcpu->arch.gprs[rt];
+
+                       kvm_debug("[%#lx] OP_GSSWX: eaddr: %#lx, gpr: %#lx, 
data: %#x\n",
+                                 vcpu->arch.pc, vcpu->arch.host_cp0_badvaddr,
+                                 vcpu->arch.gprs[rt], *(u32 *)data);
+                       break;
+               case 0x3:
+                       run->mmio.len = 8;
+                       *(u64 *)data = vcpu->arch.gprs[rt];
+
+                       kvm_debug("[%#lx] OP_GSSDX: eaddr: %#lx, gpr: %#lx, 
data: %#llx\n",
+                                 vcpu->arch.pc, vcpu->arch.host_cp0_badvaddr,
+                                 vcpu->arch.gprs[rt], *(u64 *)data);
+                       break;
+               default:
+                       kvm_err("Godson Exteneded GS-Store not yet supported 
(inst=0x%08x)\n",
+                               inst.word);
+                       break;
+               }
+               break;
+#endif
        default:
                kvm_err("Store not yet supported (inst=0x%08x)\n",
                        inst.word);
@@ -1695,6 +1901,7 @@ enum emulation_result kvm_mips_emulate_load(union 
mips_instruction inst,
        enum emulation_result er;
        unsigned long curr_pc;
        u32 op, rt;
+       unsigned int imme;
 
        rt = inst.i_format.rt;
        op = inst.i_format.opcode;
@@ -1747,6 +1954,162 @@ enum emulation_result kvm_mips_emulate_load(union 
mips_instruction inst,
                run->mmio.len = 1;
                break;
 
+       case lwl_op:
+               run->mmio.phys_addr = kvm_mips_callbacks->gva_to_gpa(
+                                       vcpu->arch.host_cp0_badvaddr) & (~0x3);
+
+               run->mmio.len = 4;
+               imme = vcpu->arch.host_cp0_badvaddr & 0x3;
+               switch (imme) {
+               case 0:
+                       vcpu->mmio_needed = 3;  /* 1 byte */
+                       break;
+               case 1:
+                       vcpu->mmio_needed = 4;  /* 2 bytes */
+                       break;
+               case 2:
+                       vcpu->mmio_needed = 5;  /* 3 bytes */
+                       break;
+               case 3:
+                       vcpu->mmio_needed = 6;  /* 4 bytes */
+                       break;
+               default:
+                       break;
+               }
+               break;
+
+       case lwr_op:
+               run->mmio.phys_addr = kvm_mips_callbacks->gva_to_gpa(
+                                       vcpu->arch.host_cp0_badvaddr) & (~0x3);
+
+               run->mmio.len = 4;
+               imme = vcpu->arch.host_cp0_badvaddr & 0x3;
+               switch (imme) {
+               case 0:
+                       vcpu->mmio_needed = 7;  /* 4 bytes */
+                       break;
+               case 1:
+                       vcpu->mmio_needed = 8;  /* 3 bytes */
+                       break;
+               case 2:
+                       vcpu->mmio_needed = 9;  /* 2 bytes */
+                       break;
+               case 3:
+                       vcpu->mmio_needed = 10; /* 1 byte */
+                       break;
+               default:
+                       break;
+               }
+               break;
+
+       case ldl_op:
+               run->mmio.phys_addr = kvm_mips_callbacks->gva_to_gpa(
+                                       vcpu->arch.host_cp0_badvaddr) & (~0x7);
+
+               run->mmio.len = 8;
+               imme = vcpu->arch.host_cp0_badvaddr & 0x7;
+               switch (imme) {
+               case 0:
+                       vcpu->mmio_needed = 11; /* 1 byte */
+                       break;
+               case 1:
+                       vcpu->mmio_needed = 12; /* 2 bytes */
+                       break;
+               case 2:
+                       vcpu->mmio_needed = 13; /* 3 bytes */
+                       break;
+               case 3:
+                       vcpu->mmio_needed = 14; /* 4 bytes */
+                       break;
+               case 4:
+                       vcpu->mmio_needed = 15; /* 5 bytes */
+                       break;
+               case 5:
+                       vcpu->mmio_needed = 16; /* 6 bytes */
+                       break;
+               case 6:
+                       vcpu->mmio_needed = 17; /* 7 bytes */
+                       break;
+               case 7:
+                       vcpu->mmio_needed = 18; /* 8 bytes */
+                       break;
+               default:
+                       break;
+               }
+               break;
+
+       case ldr_op:
+               run->mmio.phys_addr = kvm_mips_callbacks->gva_to_gpa(
+                                       vcpu->arch.host_cp0_badvaddr) & (~0x7);
+
+               run->mmio.len = 8;
+               imme = vcpu->arch.host_cp0_badvaddr & 0x7;
+               switch (imme) {
+               case 0:
+                       vcpu->mmio_needed = 19; /* 8 bytes */
+                       break;
+               case 1:
+                       vcpu->mmio_needed = 20; /* 7 bytes */
+                       break;
+               case 2:
+                       vcpu->mmio_needed = 21; /* 6 bytes */
+                       break;
+               case 3:
+                       vcpu->mmio_needed = 22; /* 5 bytes */
+                       break;
+               case 4:
+                       vcpu->mmio_needed = 23; /* 4 bytes */
+                       break;
+               case 5:
+                       vcpu->mmio_needed = 24; /* 3 bytes */
+                       break;
+               case 6:
+                       vcpu->mmio_needed = 25; /* 2 bytes */
+                       break;
+               case 7:
+                       vcpu->mmio_needed = 26; /* 1 byte */
+                       break;
+               default:
+                       break;
+               }
+               break;
+
+#ifdef CONFIG_CPU_LOONGSON64
+       case ldc2_op:
+               rt = inst.loongson3_lsdc2_format.rt;
+               switch (inst.loongson3_lsdc2_format.opcode1) {
+               /*
+                * Loongson-3 overridden ldc2 instructions.
+                * opcode1              instruction
+                *   0x0          gslbx: store 1 bytes from GPR
+                *   0x1          gslhx: store 2 bytes from GPR
+                *   0x2          gslwx: store 4 bytes from GPR
+                *   0x3          gsldx: store 8 bytes from GPR
+                */
+               case 0x0:
+                       run->mmio.len = 1;
+                       vcpu->mmio_needed = 27; /* signed */
+                       break;
+               case 0x1:
+                       run->mmio.len = 2;
+                       vcpu->mmio_needed = 28; /* signed */
+                       break;
+               case 0x2:
+                       run->mmio.len = 4;
+                       vcpu->mmio_needed = 29; /* signed */
+                       break;
+               case 0x3:
+                       run->mmio.len = 8;
+                       vcpu->mmio_needed = 30; /* signed */
+                       break;
+               default:
+                       kvm_err("Godson Exteneded GS-Load for float not yet 
supported (inst=0x%08x)\n",
+                               inst.word);
+                       break;
+               }
+               break;
+#endif
+
        default:
                kvm_err("Load not yet supported (inst=0x%08x)\n",
                        inst.word);
@@ -2612,28 +2975,125 @@ enum emulation_result 
kvm_mips_complete_mmio_load(struct kvm_vcpu *vcpu,
 
        switch (run->mmio.len) {
        case 8:
-               *gpr = *(s64 *)run->mmio.data;
+               switch (vcpu->mmio_needed) {
+               case 11:
+                       *gpr = (vcpu->arch.gprs[vcpu->arch.io_gpr] & 
0xffffffffffffff) |
+                               (((*(s64 *)run->mmio.data) & 0xff) << 56);
+                       break;
+               case 12:
+                       *gpr = (vcpu->arch.gprs[vcpu->arch.io_gpr] & 
0xffffffffffff) |
+                               (((*(s64 *)run->mmio.data) & 0xffff) << 48);
+                       break;
+               case 13:
+                       *gpr = (vcpu->arch.gprs[vcpu->arch.io_gpr] & 
0xffffffffff) |
+                               (((*(s64 *)run->mmio.data) & 0xffffff) << 40);
+                       break;
+               case 14:
+                       *gpr = (vcpu->arch.gprs[vcpu->arch.io_gpr] & 
0xffffffff) |
+                               (((*(s64 *)run->mmio.data) & 0xffffffff) << 32);
+                       break;
+               case 15:
+                       *gpr = (vcpu->arch.gprs[vcpu->arch.io_gpr] & 0xffffff) |
+                               (((*(s64 *)run->mmio.data) & 0xffffffffff) << 
24);
+                       break;
+               case 16:
+                       *gpr = (vcpu->arch.gprs[vcpu->arch.io_gpr] & 0xffff) |
+                               (((*(s64 *)run->mmio.data) & 0xffffffffffff) << 
16);
+                       break;
+               case 17:
+                       *gpr = (vcpu->arch.gprs[vcpu->arch.io_gpr] & 0xff) |
+                               (((*(s64 *)run->mmio.data) & 0xffffffffffffff) 
<< 8);
+                       break;
+               case 18:
+               case 19:
+                       *gpr = *(s64 *)run->mmio.data;
+                       break;
+               case 20:
+                       *gpr = (vcpu->arch.gprs[vcpu->arch.io_gpr] & 
0xff00000000000000) |
+                               ((((*(s64 *)run->mmio.data)) >> 8) & 
0xffffffffffffff);
+                       break;
+               case 21:
+                       *gpr = (vcpu->arch.gprs[vcpu->arch.io_gpr] & 
0xffff000000000000) |
+                               ((((*(s64 *)run->mmio.data)) >> 16) & 
0xffffffffffff);
+                       break;
+               case 22:
+                       *gpr = (vcpu->arch.gprs[vcpu->arch.io_gpr] & 
0xffffff0000000000) |
+                               ((((*(s64 *)run->mmio.data)) >> 24) & 
0xffffffffff);
+                       break;
+               case 23:
+                       *gpr = (vcpu->arch.gprs[vcpu->arch.io_gpr] & 
0xffffffff00000000) |
+                               ((((*(s64 *)run->mmio.data)) >> 32) & 
0xffffffff);
+                       break;
+               case 24:
+                       *gpr = (vcpu->arch.gprs[vcpu->arch.io_gpr] & 
0xffffffffff000000) |
+                               ((((*(s64 *)run->mmio.data)) >> 40) & 0xffffff);
+                       break;
+               case 25:
+                       *gpr = (vcpu->arch.gprs[vcpu->arch.io_gpr] & 
0xffffffffffff0000) |
+                               ((((*(s64 *)run->mmio.data)) >> 48) & 0xffff);
+                       break;
+               case 26:
+                       *gpr = (vcpu->arch.gprs[vcpu->arch.io_gpr] & 
0xffffffffffffff00) |
+                               ((((*(s64 *)run->mmio.data)) >> 56) & 0xff);
+                       break;
+               default:
+                       *gpr = *(s64 *)run->mmio.data;
+               }
                break;
 
        case 4:
-               if (vcpu->mmio_needed == 2)
-                       *gpr = *(s32 *)run->mmio.data;
-               else
+               switch (vcpu->mmio_needed) {
+               case 1:
                        *gpr = *(u32 *)run->mmio.data;
+                       break;
+               case 2:
+                       *gpr = *(s32 *)run->mmio.data;
+                       break;
+               case 3:
+                       *gpr = (vcpu->arch.gprs[vcpu->arch.io_gpr] & 0xffffff) |
+                               (((*(s32 *)run->mmio.data) & 0xff) << 24);
+                       break;
+               case 4:
+                       *gpr = (vcpu->arch.gprs[vcpu->arch.io_gpr] & 0xffff) |
+                               (((*(s32 *)run->mmio.data) & 0xffff) << 16);
+                       break;
+               case 5:
+                       *gpr = (vcpu->arch.gprs[vcpu->arch.io_gpr] & 0xff) |
+                               (((*(s32 *)run->mmio.data) & 0xffffff) << 8);
+                       break;
+               case 6:
+               case 7:
+                       *gpr = *(s32 *)run->mmio.data;
+                       break;
+               case 8:
+                       *gpr = (vcpu->arch.gprs[vcpu->arch.io_gpr] & 
0xff000000) |
+                               ((((*(s32 *)run->mmio.data)) >> 8) & 0xffffff);
+                       break;
+               case 9:
+                       *gpr = (vcpu->arch.gprs[vcpu->arch.io_gpr] & 
0xffff0000) |
+                               ((((*(s32 *)run->mmio.data)) >> 16) & 0xffff);
+                       break;
+               case 10:
+                       *gpr = (vcpu->arch.gprs[vcpu->arch.io_gpr] & 
0xffffff00) |
+                               ((((*(s32 *)run->mmio.data)) >> 24) & 0xff);
+                       break;
+               default:
+                       *gpr = *(s32 *)run->mmio.data;
+               }
                break;
 
        case 2:
-               if (vcpu->mmio_needed == 2)
-                       *gpr = *(s16 *) run->mmio.data;
-               else
+               if (vcpu->mmio_needed == 1)
                        *gpr = *(u16 *)run->mmio.data;
+               else
+                       *gpr = *(s16 *)run->mmio.data;
 
                break;
        case 1:
-               if (vcpu->mmio_needed == 2)
-                       *gpr = *(s8 *) run->mmio.data;
+               if (vcpu->mmio_needed == 1)
+                       *gpr = *(u8 *)run->mmio.data;
                else
-                       *gpr = *(u8 *) run->mmio.data;
+                       *gpr = *(s8 *)run->mmio.data;
                break;
        }
 
-- 
2.7.0


Reply via email to