KVM: PPC: Book3s: mmio: Deliver DSI after emulation failure

MMIO emulation can fail if the guest uses an instruction that we are
not prepared to emulate. Since these instructions can be and most
likely are valid ones, this is (slightly) closer to an access fault
than to an illegal instruction, so deliver a Data Storage interrupt
instead of a Program interrupt.

BookE ignores bad faults, so it will keep using a Program interrupt
because a DSI would cause a fault loop in the guest.

Suggested-by: Nicholas Piggin <npiggin@gmail.com>
Signed-off-by: Fabiano Rosas <farosas@linux.ibm.com>
Reviewed-by: Nicholas Piggin <npiggin@gmail.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/20220125215655.1026224-6-farosas@linux.ibm.com
This commit is contained in:
Fabiano Rosas 2022-01-25 18:56:55 -03:00 committed by Michael Ellerman
parent 349fbfe9b9
commit c1c8a66367
2 changed files with 25 additions and 7 deletions

View File

@ -73,7 +73,6 @@ int kvmppc_emulate_loadstore(struct kvm_vcpu *vcpu)
{
u32 inst;
enum emulation_result emulated = EMULATE_FAIL;
int advance = 1;
struct instruction_op op;
/* this default type might be overwritten by subcategories */
@ -98,6 +97,8 @@ int kvmppc_emulate_loadstore(struct kvm_vcpu *vcpu)
int type = op.type & INSTR_TYPE_MASK;
int size = GETSIZE(op.type);
vcpu->mmio_is_write = OP_IS_STORE(type);
switch (type) {
case LOAD: {
int instr_byte_swap = op.type & BYTEREV;
@ -355,15 +356,10 @@ int kvmppc_emulate_loadstore(struct kvm_vcpu *vcpu)
}
}
if (emulated == EMULATE_FAIL) {
advance = 0;
kvmppc_core_queue_program(vcpu, 0);
}
trace_kvm_ppc_instr(inst, kvmppc_get_pc(vcpu), emulated);
/* Advance past emulated instruction. */
if (advance)
if (emulated != EMULATE_FAIL)
kvmppc_set_pc(vcpu, kvmppc_get_pc(vcpu) + 4);
return emulated;

View File

@ -309,6 +309,28 @@ int kvmppc_emulate_mmio(struct kvm_vcpu *vcpu)
kvmppc_get_last_inst(vcpu, INST_GENERIC, &last_inst);
kvm_debug_ratelimited("Guest access to device memory using unsupported instruction (opcode: %#08x)\n",
last_inst);
/*
* Injecting a Data Storage here is a bit more
* accurate since the instruction that caused the
* access could still be a valid one.
*/
if (!IS_ENABLED(CONFIG_BOOKE)) {
ulong dsisr = DSISR_BADACCESS;
if (vcpu->mmio_is_write)
dsisr |= DSISR_ISSTORE;
kvmppc_core_queue_data_storage(vcpu, vcpu->arch.vaddr_accessed, dsisr);
} else {
/*
* BookE does not send a SIGBUS on a bad
* fault, so use a Program interrupt instead
* to avoid a fault loop.
*/
kvmppc_core_queue_program(vcpu, 0);
}
r = RESUME_GUEST;
break;
}