summaryrefslogtreecommitdiffstats
path: root/arch/mips
diff options
context:
space:
mode:
authorJames Hogan <james.hogan@imgtec.com>2016-10-25 11:11:12 -0400
committerPaolo Bonzini <pbonzini@redhat.com>2016-10-26 07:43:55 -0400
commite1e575f6b026734be3b1f075e780e91ab08ca541 (patch)
treec104b417fdee65ed848470a75cc459532df9cf98 /arch/mips
parentede5f3e7b54a4347be4d8525269eae50902bd7cd (diff)
KVM: MIPS: Precalculate MMIO load resume PC
The advancing of the PC when completing an MMIO load is done before re-entering the guest, i.e. before restoring the guest ASID. However if the load is in a branch delay slot it may need to access guest code to read the prior branch instruction. This isn't safe in TLB mapped code at the moment, nor in the future when we'll access unmapped guest segments using direct user accessors too, as it could read the branch from host user memory instead. Therefore calculate the resume PC in advance while we're still in the right context and save it in the new vcpu->arch.io_pc (replacing the no longer needed vcpu->arch.pending_load_cause), and restore it on MMIO completion. Fixes: e685c689f3a8 ("KVM/MIPS32: Privileged instruction/target branch 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 Cc: <stable@vger.kernel.org> # 3.10.x- Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Diffstat (limited to 'arch/mips')
-rw-r--r--arch/mips/include/asm/kvm_host.h7
-rw-r--r--arch/mips/kvm/emulate.c24
2 files changed, 19 insertions, 12 deletions
diff --git a/arch/mips/include/asm/kvm_host.h b/arch/mips/include/asm/kvm_host.h
index 07f58cfc1ab9..bebec370324f 100644
--- a/arch/mips/include/asm/kvm_host.h
+++ b/arch/mips/include/asm/kvm_host.h
@@ -293,7 +293,10 @@ struct kvm_vcpu_arch {
293 /* Host KSEG0 address of the EI/DI offset */ 293 /* Host KSEG0 address of the EI/DI offset */
294 void *kseg0_commpage; 294 void *kseg0_commpage;
295 295
296 u32 io_gpr; /* GPR used as IO source/target */ 296 /* Resume PC after MMIO completion */
297 unsigned long io_pc;
298 /* GPR used as IO source/target */
299 u32 io_gpr;
297 300
298 struct hrtimer comparecount_timer; 301 struct hrtimer comparecount_timer;
299 /* Count timer control KVM register */ 302 /* Count timer control KVM register */
@@ -315,8 +318,6 @@ struct kvm_vcpu_arch {
315 /* Bitmask of pending exceptions to be cleared */ 318 /* Bitmask of pending exceptions to be cleared */
316 unsigned long pending_exceptions_clr; 319 unsigned long pending_exceptions_clr;
317 320
318 u32 pending_load_cause;
319
320 /* Save/Restore the entryhi register when are are preempted/scheduled back in */ 321 /* Save/Restore the entryhi register when are are preempted/scheduled back in */
321 unsigned long preempt_entryhi; 322 unsigned long preempt_entryhi;
322 323
diff --git a/arch/mips/kvm/emulate.c b/arch/mips/kvm/emulate.c
index c45ef0f13dfa..aa0937423e28 100644
--- a/arch/mips/kvm/emulate.c
+++ b/arch/mips/kvm/emulate.c
@@ -1528,13 +1528,25 @@ enum emulation_result kvm_mips_emulate_load(union mips_instruction inst,
1528 struct kvm_vcpu *vcpu) 1528 struct kvm_vcpu *vcpu)
1529{ 1529{
1530 enum emulation_result er = EMULATE_DO_MMIO; 1530 enum emulation_result er = EMULATE_DO_MMIO;
1531 unsigned long curr_pc;
1531 u32 op, rt; 1532 u32 op, rt;
1532 u32 bytes; 1533 u32 bytes;
1533 1534
1534 rt = inst.i_format.rt; 1535 rt = inst.i_format.rt;
1535 op = inst.i_format.opcode; 1536 op = inst.i_format.opcode;
1536 1537
1537 vcpu->arch.pending_load_cause = cause; 1538 /*
1539 * Find the resume PC now while we have safe and easy access to the
1540 * prior branch instruction, and save it for
1541 * kvm_mips_complete_mmio_load() to restore later.
1542 */
1543 curr_pc = vcpu->arch.pc;
1544 er = update_pc(vcpu, cause);
1545 if (er == EMULATE_FAIL)
1546 return er;
1547 vcpu->arch.io_pc = vcpu->arch.pc;
1548 vcpu->arch.pc = curr_pc;
1549
1538 vcpu->arch.io_gpr = rt; 1550 vcpu->arch.io_gpr = rt;
1539 1551
1540 switch (op) { 1552 switch (op) {
@@ -2494,9 +2506,8 @@ enum emulation_result kvm_mips_complete_mmio_load(struct kvm_vcpu *vcpu,
2494 goto done; 2506 goto done;
2495 } 2507 }
2496 2508
2497 er = update_pc(vcpu, vcpu->arch.pending_load_cause); 2509 /* Restore saved resume PC */
2498 if (er == EMULATE_FAIL) 2510 vcpu->arch.pc = vcpu->arch.io_pc;
2499 return er;
2500 2511
2501 switch (run->mmio.len) { 2512 switch (run->mmio.len) {
2502 case 4: 2513 case 4:
@@ -2518,11 +2529,6 @@ enum emulation_result kvm_mips_complete_mmio_load(struct kvm_vcpu *vcpu,
2518 break; 2529 break;
2519 } 2530 }
2520 2531
2521 if (vcpu->arch.pending_load_cause & CAUSEF_BD)
2522 kvm_debug("[%#lx] Completing %d byte BD Load to gpr %d (0x%08lx) type %d\n",
2523 vcpu->arch.pc, run->mmio.len, vcpu->arch.io_gpr, *gpr,
2524 vcpu->mmio_needed);
2525
2526done: 2532done:
2527 return er; 2533 return er;
2528} 2534}