Commit 8b48d5b7 authored by James Hogan's avatar James Hogan

KVM: MIPS/Emulate: De-duplicate MMIO emulation

Refactor MIPS KVM MMIO load/store emulation to reduce code duplication.
Each duplicate differed slightly anyway, and it will simplify adding
64-bit MMIO support for VZ.

kvm_mips_emulate_store() and kvm_mips_emulate_load() can now return
EMULATE_DO_MMIO (as possibly originally intended). We therefore stop
calling either of these from kvm_mips_emulate_inst(), which is now only
used by kvm_trap_emul_handle_cop_unusable() which is picky about return
values.
Signed-off-by: default avatarJames Hogan <james.hogan@imgtec.com>
Cc: Paolo Bonzini <pbonzini@redhat.com>
Cc: "Radim Krčmář" <rkrcmar@redhat.com>
Cc: Ralf Baechle <ralf@linux-mips.org>
Cc: linux-mips@linux-mips.org
Cc: kvm@vger.kernel.org
parent 955d8dc3
...@@ -1477,9 +1477,8 @@ enum emulation_result kvm_mips_emulate_store(union mips_instruction inst, ...@@ -1477,9 +1477,8 @@ enum emulation_result kvm_mips_emulate_store(union mips_instruction inst,
struct kvm_run *run, struct kvm_run *run,
struct kvm_vcpu *vcpu) struct kvm_vcpu *vcpu)
{ {
enum emulation_result er = EMULATE_DO_MMIO; enum emulation_result er;
u32 rt; u32 rt;
u32 bytes;
void *data = run->mmio.data; void *data = run->mmio.data;
unsigned long curr_pc; unsigned long curr_pc;
...@@ -1494,103 +1493,63 @@ enum emulation_result kvm_mips_emulate_store(union mips_instruction inst, ...@@ -1494,103 +1493,63 @@ enum emulation_result kvm_mips_emulate_store(union mips_instruction inst,
rt = inst.i_format.rt; rt = inst.i_format.rt;
switch (inst.i_format.opcode) { run->mmio.phys_addr = kvm_mips_callbacks->gva_to_gpa(
case sb_op: vcpu->arch.host_cp0_badvaddr);
bytes = 1; if (run->mmio.phys_addr == KVM_INVALID_ADDR)
if (bytes > sizeof(run->mmio.data)) { goto out_fail;
kvm_err("%s: bad MMIO length: %d\n", __func__,
run->mmio.len);
}
run->mmio.phys_addr =
kvm_mips_callbacks->gva_to_gpa(vcpu->arch.
host_cp0_badvaddr);
if (run->mmio.phys_addr == KVM_INVALID_ADDR) {
er = EMULATE_FAIL;
break;
}
run->mmio.len = bytes;
run->mmio.is_write = 1;
vcpu->mmio_needed = 1;
vcpu->mmio_is_write = 1;
*(u8 *) data = vcpu->arch.gprs[rt];
kvm_debug("OP_SB: eaddr: %#lx, gpr: %#lx, data: %#x\n",
vcpu->arch.host_cp0_badvaddr, vcpu->arch.gprs[rt],
*(u8 *) data);
break;
switch (inst.i_format.opcode) {
case sw_op: case sw_op:
bytes = 4; run->mmio.len = 4;
if (bytes > sizeof(run->mmio.data)) { *(u32 *)data = vcpu->arch.gprs[rt];
kvm_err("%s: bad MMIO length: %d\n", __func__,
run->mmio.len);
}
run->mmio.phys_addr =
kvm_mips_callbacks->gva_to_gpa(vcpu->arch.
host_cp0_badvaddr);
if (run->mmio.phys_addr == KVM_INVALID_ADDR) {
er = EMULATE_FAIL;
break;
}
run->mmio.len = bytes;
run->mmio.is_write = 1;
vcpu->mmio_needed = 1;
vcpu->mmio_is_write = 1;
*(u32 *) data = vcpu->arch.gprs[rt];
kvm_debug("[%#lx] OP_SW: eaddr: %#lx, gpr: %#lx, data: %#x\n", kvm_debug("[%#lx] OP_SW: eaddr: %#lx, gpr: %#lx, data: %#x\n",
vcpu->arch.pc, vcpu->arch.host_cp0_badvaddr, vcpu->arch.pc, vcpu->arch.host_cp0_badvaddr,
vcpu->arch.gprs[rt], *(u32 *) data); vcpu->arch.gprs[rt], *(u32 *)data);
break; break;
case sh_op: case sh_op:
bytes = 2; run->mmio.len = 2;
if (bytes > sizeof(run->mmio.data)) { *(u16 *)data = vcpu->arch.gprs[rt];
kvm_err("%s: bad MMIO length: %d\n", __func__,
run->mmio.len);
}
run->mmio.phys_addr =
kvm_mips_callbacks->gva_to_gpa(vcpu->arch.
host_cp0_badvaddr);
if (run->mmio.phys_addr == KVM_INVALID_ADDR) {
er = EMULATE_FAIL;
break;
}
run->mmio.len = bytes;
run->mmio.is_write = 1;
vcpu->mmio_needed = 1;
vcpu->mmio_is_write = 1;
*(u16 *) data = vcpu->arch.gprs[rt];
kvm_debug("[%#lx] OP_SH: eaddr: %#lx, gpr: %#lx, data: %#x\n", kvm_debug("[%#lx] OP_SH: eaddr: %#lx, gpr: %#lx, data: %#x\n",
vcpu->arch.pc, vcpu->arch.host_cp0_badvaddr, vcpu->arch.pc, vcpu->arch.host_cp0_badvaddr,
vcpu->arch.gprs[rt], *(u32 *) data); 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; break;
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);
er = EMULATE_FAIL; goto out_fail;
break;
} }
/* Rollback PC if emulation was unsuccessful */ run->mmio.is_write = 1;
if (er == EMULATE_FAIL) vcpu->mmio_needed = 1;
vcpu->arch.pc = curr_pc; vcpu->mmio_is_write = 1;
return EMULATE_DO_MMIO;
return er; out_fail:
/* Rollback PC if emulation was unsuccessful */
vcpu->arch.pc = curr_pc;
return EMULATE_FAIL;
} }
enum emulation_result kvm_mips_emulate_load(union mips_instruction inst, enum emulation_result kvm_mips_emulate_load(union mips_instruction inst,
u32 cause, struct kvm_run *run, u32 cause, struct kvm_run *run,
struct kvm_vcpu *vcpu) struct kvm_vcpu *vcpu)
{ {
enum emulation_result er = EMULATE_DO_MMIO; enum emulation_result er;
unsigned long curr_pc; unsigned long curr_pc;
u32 op, rt; u32 op, rt;
u32 bytes;
rt = inst.i_format.rt; rt = inst.i_format.rt;
op = inst.i_format.opcode; op = inst.i_format.opcode;
...@@ -1609,94 +1568,41 @@ enum emulation_result kvm_mips_emulate_load(union mips_instruction inst, ...@@ -1609,94 +1568,41 @@ enum emulation_result kvm_mips_emulate_load(union mips_instruction inst,
vcpu->arch.io_gpr = rt; vcpu->arch.io_gpr = rt;
run->mmio.phys_addr = kvm_mips_callbacks->gva_to_gpa(
vcpu->arch.host_cp0_badvaddr);
if (run->mmio.phys_addr == KVM_INVALID_ADDR)
return EMULATE_FAIL;
vcpu->mmio_needed = 2; /* signed */
switch (op) { switch (op) {
case lw_op: case lw_op:
bytes = 4; run->mmio.len = 4;
if (bytes > sizeof(run->mmio.data)) {
kvm_err("%s: bad MMIO length: %d\n", __func__,
run->mmio.len);
er = EMULATE_FAIL;
break;
}
run->mmio.phys_addr =
kvm_mips_callbacks->gva_to_gpa(vcpu->arch.
host_cp0_badvaddr);
if (run->mmio.phys_addr == KVM_INVALID_ADDR) {
er = EMULATE_FAIL;
break;
}
run->mmio.len = bytes;
run->mmio.is_write = 0;
vcpu->mmio_needed = 1;
vcpu->mmio_is_write = 0;
break; break;
case lh_op:
case lhu_op: case lhu_op:
bytes = 2; vcpu->mmio_needed = 1; /* unsigned */
if (bytes > sizeof(run->mmio.data)) { /* fall through */
kvm_err("%s: bad MMIO length: %d\n", __func__, case lh_op:
run->mmio.len); run->mmio.len = 2;
er = EMULATE_FAIL;
break;
}
run->mmio.phys_addr =
kvm_mips_callbacks->gva_to_gpa(vcpu->arch.
host_cp0_badvaddr);
if (run->mmio.phys_addr == KVM_INVALID_ADDR) {
er = EMULATE_FAIL;
break;
}
run->mmio.len = bytes;
run->mmio.is_write = 0;
vcpu->mmio_needed = 1;
vcpu->mmio_is_write = 0;
if (op == lh_op)
vcpu->mmio_needed = 2;
else
vcpu->mmio_needed = 1;
break; break;
case lbu_op: case lbu_op:
vcpu->mmio_needed = 1; /* unsigned */
/* fall through */
case lb_op: case lb_op:
bytes = 1; run->mmio.len = 1;
if (bytes > sizeof(run->mmio.data)) {
kvm_err("%s: bad MMIO length: %d\n", __func__,
run->mmio.len);
er = EMULATE_FAIL;
break;
}
run->mmio.phys_addr =
kvm_mips_callbacks->gva_to_gpa(vcpu->arch.
host_cp0_badvaddr);
if (run->mmio.phys_addr == KVM_INVALID_ADDR) {
er = EMULATE_FAIL;
break;
}
run->mmio.len = bytes;
run->mmio.is_write = 0;
vcpu->mmio_is_write = 0;
if (op == lb_op)
vcpu->mmio_needed = 2;
else
vcpu->mmio_needed = 1;
break; break;
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);
er = EMULATE_FAIL; vcpu->mmio_needed = 0;
break; return EMULATE_FAIL;
} }
return er; run->mmio.is_write = 0;
vcpu->mmio_is_write = 0;
return EMULATE_DO_MMIO;
} }
static enum emulation_result kvm_mips_guest_cache_op(int (*fn)(unsigned long), static enum emulation_result kvm_mips_guest_cache_op(int (*fn)(unsigned long),
...@@ -1873,18 +1779,6 @@ enum emulation_result kvm_mips_emulate_inst(u32 cause, u32 *opc, ...@@ -1873,18 +1779,6 @@ enum emulation_result kvm_mips_emulate_inst(u32 cause, u32 *opc,
case cop0_op: case cop0_op:
er = kvm_mips_emulate_CP0(inst, opc, cause, run, vcpu); er = kvm_mips_emulate_CP0(inst, opc, cause, run, vcpu);
break; break;
case sb_op:
case sh_op:
case sw_op:
er = kvm_mips_emulate_store(inst, cause, run, vcpu);
break;
case lb_op:
case lbu_op:
case lhu_op:
case lh_op:
case lw_op:
er = kvm_mips_emulate_load(inst, cause, run, vcpu);
break;
#ifndef CONFIG_CPU_MIPSR6 #ifndef CONFIG_CPU_MIPSR6
case cache_op: case cache_op:
......
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