mirror of
https://github.com/torvalds/linux.git
synced 2024-12-04 18:13:04 +00:00
KVM: MIPS: Use CP0_BadInstr[P] for emulation
When exiting from the guest, store the values of the CP0_BadInstr and CP0_BadInstrP registers if they exist, which contain the encodings of the instructions which caused the last synchronous exception. When the instruction is needed for emulation, kvm_get_badinstr() and kvm_get_badinstrp() are used instead of calling kvm_get_inst() directly, to decide whether to read the saved CP0_BadInstr/CP0_BadInstrP registers (if they exist), or read the instruction from memory (if not). The use of these registers should be more robust than using kvm_get_inst(), as it actually gives the instruction encoding seen by the hardware rather than relying on user accessors after the fact, which can be fooled by incoherent icache or a racing code modification. It will also work with VZ, where the guest virtual memory isn't directly accessible by the host with user accessors. Signed-off-by: James 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
This commit is contained in:
parent
122e51d474
commit
6a97c775ff
@ -280,6 +280,8 @@ struct kvm_vcpu_arch {
|
||||
unsigned long host_cp0_badvaddr;
|
||||
unsigned long host_cp0_epc;
|
||||
u32 host_cp0_cause;
|
||||
u32 host_cp0_badinstr;
|
||||
u32 host_cp0_badinstrp;
|
||||
|
||||
/* GPRS */
|
||||
unsigned long gprs[32];
|
||||
@ -641,6 +643,8 @@ void kvm_trap_emul_invalidate_gva(struct kvm_vcpu *vcpu, unsigned long addr,
|
||||
/* Emulation */
|
||||
int kvm_get_inst(u32 *opc, struct kvm_vcpu *vcpu, u32 *out);
|
||||
enum emulation_result update_pc(struct kvm_vcpu *vcpu, u32 cause);
|
||||
int kvm_get_badinstr(u32 *opc, struct kvm_vcpu *vcpu, u32 *out);
|
||||
int kvm_get_badinstrp(u32 *opc, struct kvm_vcpu *vcpu, u32 *out);
|
||||
|
||||
/**
|
||||
* kvm_is_ifetch_fault() - Find whether a TLBL exception is due to ifetch fault.
|
||||
|
@ -54,7 +54,7 @@ static int kvm_compute_return_epc(struct kvm_vcpu *vcpu, unsigned long instpc,
|
||||
}
|
||||
|
||||
/* Read the instruction */
|
||||
err = kvm_get_inst((u32 *)epc, vcpu, &insn.word);
|
||||
err = kvm_get_badinstrp((u32 *)epc, vcpu, &insn.word);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@ -258,6 +258,48 @@ enum emulation_result update_pc(struct kvm_vcpu *vcpu, u32 cause)
|
||||
return EMULATE_DONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* kvm_get_badinstr() - Get bad instruction encoding.
|
||||
* @opc: Guest pointer to faulting instruction.
|
||||
* @vcpu: KVM VCPU information.
|
||||
*
|
||||
* Gets the instruction encoding of the faulting instruction, using the saved
|
||||
* BadInstr register value if it exists, otherwise falling back to reading guest
|
||||
* memory at @opc.
|
||||
*
|
||||
* Returns: The instruction encoding of the faulting instruction.
|
||||
*/
|
||||
int kvm_get_badinstr(u32 *opc, struct kvm_vcpu *vcpu, u32 *out)
|
||||
{
|
||||
if (cpu_has_badinstr) {
|
||||
*out = vcpu->arch.host_cp0_badinstr;
|
||||
return 0;
|
||||
} else {
|
||||
return kvm_get_inst(opc, vcpu, out);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* kvm_get_badinstrp() - Get bad prior instruction encoding.
|
||||
* @opc: Guest pointer to prior faulting instruction.
|
||||
* @vcpu: KVM VCPU information.
|
||||
*
|
||||
* Gets the instruction encoding of the prior faulting instruction (the branch
|
||||
* containing the delay slot which faulted), using the saved BadInstrP register
|
||||
* value if it exists, otherwise falling back to reading guest memory at @opc.
|
||||
*
|
||||
* Returns: The instruction encoding of the prior faulting instruction.
|
||||
*/
|
||||
int kvm_get_badinstrp(u32 *opc, struct kvm_vcpu *vcpu, u32 *out)
|
||||
{
|
||||
if (cpu_has_badinstrp) {
|
||||
*out = vcpu->arch.host_cp0_badinstrp;
|
||||
return 0;
|
||||
} else {
|
||||
return kvm_get_inst(opc, vcpu, out);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* kvm_mips_count_disabled() - Find whether the CP0_Count timer is disabled.
|
||||
* @vcpu: Virtual CPU.
|
||||
@ -1839,7 +1881,7 @@ enum emulation_result kvm_mips_emulate_inst(u32 cause, u32 *opc,
|
||||
/* Fetch the instruction. */
|
||||
if (cause & CAUSEF_BD)
|
||||
opc += 1;
|
||||
err = kvm_get_inst(opc, vcpu, &inst.word);
|
||||
err = kvm_get_badinstr(opc, vcpu, &inst.word);
|
||||
if (err)
|
||||
return EMULATE_FAIL;
|
||||
|
||||
@ -2434,7 +2476,7 @@ enum emulation_result kvm_mips_handle_ri(u32 cause, u32 *opc,
|
||||
/* Fetch the instruction. */
|
||||
if (cause & CAUSEF_BD)
|
||||
opc += 1;
|
||||
err = kvm_get_inst(opc, vcpu, &inst.word);
|
||||
err = kvm_get_badinstr(opc, vcpu, &inst.word);
|
||||
if (err) {
|
||||
kvm_err("%s: Cannot get inst @ %p (%d)\n", __func__, opc, err);
|
||||
return EMULATE_FAIL;
|
||||
|
@ -53,6 +53,8 @@
|
||||
/* Some CP0 registers */
|
||||
#define C0_HWRENA 7, 0
|
||||
#define C0_BADVADDR 8, 0
|
||||
#define C0_BADINSTR 8, 1
|
||||
#define C0_BADINSTRP 8, 2
|
||||
#define C0_ENTRYHI 10, 0
|
||||
#define C0_STATUS 12, 0
|
||||
#define C0_CAUSE 13, 0
|
||||
@ -579,6 +581,18 @@ void *kvm_mips_build_exit(void *addr)
|
||||
uasm_i_mfc0(&p, K0, C0_CAUSE);
|
||||
uasm_i_sw(&p, K0, offsetof(struct kvm_vcpu_arch, host_cp0_cause), K1);
|
||||
|
||||
if (cpu_has_badinstr) {
|
||||
uasm_i_mfc0(&p, K0, C0_BADINSTR);
|
||||
uasm_i_sw(&p, K0, offsetof(struct kvm_vcpu_arch,
|
||||
host_cp0_badinstr), K1);
|
||||
}
|
||||
|
||||
if (cpu_has_badinstrp) {
|
||||
uasm_i_mfc0(&p, K0, C0_BADINSTRP);
|
||||
uasm_i_sw(&p, K0, offsetof(struct kvm_vcpu_arch,
|
||||
host_cp0_badinstrp), K1);
|
||||
}
|
||||
|
||||
/* Now restore the host state just enough to run the handlers */
|
||||
|
||||
/* Switch EBASE to the one used by Linux */
|
||||
|
@ -1471,7 +1471,7 @@ int kvm_mips_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu)
|
||||
if (cause & CAUSEF_BD)
|
||||
opc += 1;
|
||||
inst = 0;
|
||||
kvm_get_inst(opc, vcpu, &inst);
|
||||
kvm_get_badinstr(opc, vcpu, &inst);
|
||||
kvm_err("Exception Code: %d, not yet handled, @ PC: %p, inst: 0x%08x BadVaddr: %#lx Status: %#lx\n",
|
||||
exccode, opc, inst, badvaddr,
|
||||
kvm_read_c0_guest_status(vcpu->arch.cop0));
|
||||
|
Loading…
Reference in New Issue
Block a user