Commit dc6d95b1 authored by Huacai Chen's avatar Huacai Chen Committed by Paolo Bonzini

KVM: MIPS: Add more MMIO load/store instructions emulation

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.
Reviewed-by: default avatarAleksandar Markovic <aleksandar.qemu.devel@gmail.com>
Signed-off-by: default avatarHuacai Chen <chenhc@lemote.com>
Co-developed-by: default avatarJiaxun Yang <jiaxun.yang@flygoat.com>
Message-Id: <1590220602-3547-14-git-send-email-chenhc@lemote.com>
Signed-off-by: default avatarPaolo Bonzini <pbonzini@redhat.com>
parent 8a5097ee
...@@ -1604,6 +1604,7 @@ enum emulation_result kvm_mips_emulate_store(union mips_instruction inst, ...@@ -1604,6 +1604,7 @@ enum emulation_result kvm_mips_emulate_store(union mips_instruction inst,
enum emulation_result er; enum emulation_result er;
u32 rt; u32 rt;
void *data = run->mmio.data; void *data = run->mmio.data;
unsigned int imme;
unsigned long curr_pc; unsigned long curr_pc;
/* /*
...@@ -1661,6 +1662,211 @@ enum emulation_result kvm_mips_emulate_store(union mips_instruction inst, ...@@ -1661,6 +1662,211 @@ enum emulation_result kvm_mips_emulate_store(union mips_instruction inst,
vcpu->arch.gprs[rt], *(u8 *)data); vcpu->arch.gprs[rt], *(u8 *)data);
break; 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: default:
kvm_err("Store not yet supported (inst=0x%08x)\n", kvm_err("Store not yet supported (inst=0x%08x)\n",
inst.word); inst.word);
...@@ -1695,6 +1901,7 @@ enum emulation_result kvm_mips_emulate_load(union mips_instruction inst, ...@@ -1695,6 +1901,7 @@ enum emulation_result kvm_mips_emulate_load(union mips_instruction inst,
enum emulation_result er; enum emulation_result er;
unsigned long curr_pc; unsigned long curr_pc;
u32 op, rt; u32 op, rt;
unsigned int imme;
rt = inst.i_format.rt; rt = inst.i_format.rt;
op = inst.i_format.opcode; op = inst.i_format.opcode;
...@@ -1747,6 +1954,162 @@ enum emulation_result kvm_mips_emulate_load(union mips_instruction inst, ...@@ -1747,6 +1954,162 @@ enum emulation_result kvm_mips_emulate_load(union mips_instruction inst,
run->mmio.len = 1; run->mmio.len = 1;
break; 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: default:
kvm_err("Load not yet supported (inst=0x%08x)\n", kvm_err("Load not yet supported (inst=0x%08x)\n",
inst.word); inst.word);
...@@ -2612,28 +2975,125 @@ enum emulation_result kvm_mips_complete_mmio_load(struct kvm_vcpu *vcpu, ...@@ -2612,28 +2975,125 @@ enum emulation_result kvm_mips_complete_mmio_load(struct kvm_vcpu *vcpu,
switch (run->mmio.len) { switch (run->mmio.len) {
case 8: 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; break;
case 4: case 4:
if (vcpu->mmio_needed == 2) switch (vcpu->mmio_needed) {
*gpr = *(s32 *)run->mmio.data; case 1:
else
*gpr = *(u32 *)run->mmio.data; *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; break;
case 2: case 2:
if (vcpu->mmio_needed == 2) if (vcpu->mmio_needed == 1)
*gpr = *(s16 *) run->mmio.data;
else
*gpr = *(u16 *)run->mmio.data; *gpr = *(u16 *)run->mmio.data;
else
*gpr = *(s16 *)run->mmio.data;
break; break;
case 1: case 1:
if (vcpu->mmio_needed == 2) if (vcpu->mmio_needed == 1)
*gpr = *(s8 *) run->mmio.data; *gpr = *(u8 *)run->mmio.data;
else else
*gpr = *(u8 *) run->mmio.data; *gpr = *(s8 *)run->mmio.data;
break; break;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment