KVM: MIPS: Emulate MMIO via TLB miss for EVA
authorJames Hogan <james.hogan@imgtec.com>
Fri, 19 Aug 2016 14:09:47 +0000 (15:09 +0100)
committerJames Hogan <james.hogan@imgtec.com>
Thu, 8 Sep 2016 17:29:54 +0000 (18:29 +0100)
MIPS Enhanced Virtual Addressing (EVA) allows the virtual memory
segments to be rearranged such that the KSeg0/KSeg1 segments are
accessible TLB mapped to user mode, which would trigger a TLB Miss
exception (due to lack of TLB mappings) instead of an Address Error
exception.

Update the TLB Miss handling similar to Address Error handling for guest
MMIO emulation.

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/kvm/trap_emul.c

index 0915539..3a5484f 100644 (file)
@@ -175,6 +175,24 @@ static int kvm_trap_emul_handle_tlb_miss(struct kvm_vcpu *vcpu, bool store)
                        run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
                        ret = RESUME_HOST;
                }
+       } else if (KVM_GUEST_KERNEL_MODE(vcpu)
+                  && (KSEGX(badvaddr) == CKSEG0 || KSEGX(badvaddr) == CKSEG1)) {
+               /*
+                * With EVA we may get a TLB exception instead of an address
+                * error when the guest performs MMIO to KSeg1 addresses.
+                */
+               kvm_debug("Emulate %s MMIO space\n",
+                         store ? "Store to" : "Load from");
+               er = kvm_mips_emulate_inst(cause, opc, run, vcpu);
+               if (er == EMULATE_FAIL) {
+                       kvm_err("Emulate %s MMIO space failed\n",
+                               store ? "Store to" : "Load from");
+                       run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
+                       ret = RESUME_HOST;
+               } else {
+                       run->exit_reason = KVM_EXIT_MMIO;
+                       ret = RESUME_HOST;
+               }
        } else {
                kvm_err("Illegal TLB %s fault address , cause %#x, PC: %p, BadVaddr: %#lx\n",
                        store ? "ST" : "LD", cause, opc, badvaddr);