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];
/* 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.
}
/* 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;
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.
/* 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;
/* 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;
/* 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
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 */
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));