KVM: MIPS: Use CP0_BadInstr[P] for emulation
authorJames Hogan <james.hogan@imgtec.com>
Thu, 23 Apr 2015 15:54:35 +0000 (16:54 +0100)
committerJames Hogan <james.hogan@imgtec.com>
Fri, 3 Feb 2017 15:21:07 +0000 (15:21 +0000)
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
arch/mips/include/asm/kvm_host.h
arch/mips/kvm/emulate.c
arch/mips/kvm/entry.c
arch/mips/kvm/mips.c

index f296ebeda9e3caea120589f8a1a15a6050e93093..17c5e53ef3fab997e5978a186642970bb3fd596c 100644 (file)
@@ -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.
index b906fc0589f3c7d201d85744c3a465837a39ee51..b295a4a1496fdae251c1a47ce6bbd30945848250 100644 (file)
@@ -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;
index 1ae33e0e675ce80af0d6fcca40c9a535c56fd0f8..c5b254c4d0da89102a5c26964d9d567afe80c093 100644 (file)
@@ -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 */
index 29afd96069ef831ea3e897d9d7d68e85638d773c..b8f04070bf39b997f36f1e7da8c9212ea82257d8 100644 (file)
@@ -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));