in kvm/emulate.c [970:1268]
enum emulation_result kvm_mips_emulate_store(union mips_instruction inst,
u32 cause,
struct kvm_vcpu *vcpu)
{
int r;
enum emulation_result er;
u32 rt;
struct kvm_run *run = vcpu->run;
void *data = run->mmio.data;
unsigned int imme;
unsigned long curr_pc;
/*
* 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;
rt = inst.i_format.rt;
run->mmio.phys_addr = kvm_mips_callbacks->gva_to_gpa(
vcpu->arch.host_cp0_badvaddr);
if (run->mmio.phys_addr == KVM_INVALID_ADDR)
goto out_fail;
switch (inst.i_format.opcode) {
#if defined(CONFIG_64BIT)
case sd_op:
run->mmio.len = 8;
*(u64 *)data = vcpu->arch.gprs[rt];
kvm_debug("[%#lx] OP_SD: eaddr: %#lx, gpr: %#lx, data: %#llx\n",
vcpu->arch.pc, vcpu->arch.host_cp0_badvaddr,
vcpu->arch.gprs[rt], *(u64 *)data);
break;
#endif
case sw_op:
run->mmio.len = 4;
*(u32 *)data = vcpu->arch.gprs[rt];
kvm_debug("[%#lx] OP_SW: eaddr: %#lx, gpr: %#lx, data: %#x\n",
vcpu->arch.pc, vcpu->arch.host_cp0_badvaddr,
vcpu->arch.gprs[rt], *(u32 *)data);
break;
case sh_op:
run->mmio.len = 2;
*(u16 *)data = vcpu->arch.gprs[rt];
kvm_debug("[%#lx] OP_SH: eaddr: %#lx, gpr: %#lx, data: %#x\n",
vcpu->arch.pc, vcpu->arch.host_cp0_badvaddr,
vcpu->arch.gprs[rt], *(u16 *)data);
break;
case sb_op:
run->mmio.len = 1;
*(u8 *)data = vcpu->arch.gprs[rt];
kvm_debug("[%#lx] OP_SB: eaddr: %#lx, gpr: %#lx, data: %#x\n",
vcpu->arch.pc, vcpu->arch.host_cp0_badvaddr,
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;
#if defined(CONFIG_64BIT)
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;
#endif
#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 Extended 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);
goto out_fail;
}
vcpu->mmio_needed = 1;
run->mmio.is_write = 1;
vcpu->mmio_is_write = 1;
r = kvm_io_bus_write(vcpu, KVM_MMIO_BUS,
run->mmio.phys_addr, run->mmio.len, data);
if (!r) {
vcpu->mmio_needed = 0;
return EMULATE_DONE;
}
return EMULATE_DO_MMIO;
out_fail:
/* Rollback PC if emulation was unsuccessful */
vcpu->arch.pc = curr_pc;
return EMULATE_FAIL;
}