From 356d4c2040539a6c7ff75723b9503bb8ddc9cb07 Mon Sep 17 00:00:00 2001 From: Deng-Cheng Zhu Date: Thu, 29 May 2014 12:12:22 -0700 Subject: MIPS: KVM: remove the stale memory alias support function unalias_gfn The memory alias support has been removed since a1f4d39500 (KVM: Remove memory alias support). So remove unalias_gfn from the MIPS port. Reviewed-by: James Hogan Signed-off-by: Deng-Cheng Zhu Signed-off-by: Paolo Bonzini --- arch/mips/kvm/kvm_mips.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'arch/mips') diff --git a/arch/mips/kvm/kvm_mips.c b/arch/mips/kvm/kvm_mips.c index da5186fbd77a..a0d1e7105c4e 100644 --- a/arch/mips/kvm/kvm_mips.c +++ b/arch/mips/kvm/kvm_mips.c @@ -61,11 +61,6 @@ static int kvm_mips_reset_vcpu(struct kvm_vcpu *vcpu) return 0; } -gfn_t unalias_gfn(struct kvm *kvm, gfn_t gfn) -{ - return gfn; -} - /* XXXKYMA: We are simulatoring a processor that has the WII bit set in Config7, so we * are "runnable" if interrupts are pending */ -- cgit v1.2.3 From 7006e2dfda9adfa40251093604db76d7e44263b3 Mon Sep 17 00:00:00 2001 From: James Hogan Date: Thu, 29 May 2014 10:16:23 +0100 Subject: MIPS: KVM: Allocate at least 16KB for exception handlers Each MIPS KVM guest has its own copy of the KVM exception vector. This contains the TLB refill exception handler at offset 0x000, the general exception handler at offset 0x180, and interrupt exception handlers at offset 0x200 in case Cause_IV=1. A common handler is copied to offset 0x2000 and offset 0x3000 is used for temporarily storing k1 during entry from guest. However the amount of memory allocated for this purpose is calculated as 0x200 rounded up to the next page boundary, which is insufficient if 4KB pages are in use. This can lead to the common handler at offset 0x2000 being overwritten and infinitely recursive exceptions on the next exit from the guest. Increase the minimum size from 0x200 to 0x4000 to cover the full use of the page. Signed-off-by: James Hogan Cc: Paolo Bonzini Cc: Gleb Natapov Cc: kvm@vger.kernel.org Cc: Ralf Baechle Cc: linux-mips@linux-mips.org Cc: Sanjay Lal Cc: stable@vger.kernel.org Signed-off-by: Paolo Bonzini --- arch/mips/kvm/kvm_mips.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'arch/mips') diff --git a/arch/mips/kvm/kvm_mips.c b/arch/mips/kvm/kvm_mips.c index a0d1e7105c4e..1e12762ae7ea 100644 --- a/arch/mips/kvm/kvm_mips.c +++ b/arch/mips/kvm/kvm_mips.c @@ -299,7 +299,7 @@ struct kvm_vcpu *kvm_arch_vcpu_create(struct kvm *kvm, unsigned int id) if (cpu_has_veic || cpu_has_vint) { size = 0x200 + VECTORSPACING * 64; } else { - size = 0x200; + size = 0x4000; } /* Save Linux EBASE */ -- cgit v1.2.3 From 90f91356c7d67ddd98d817838df69335cb831eaa Mon Sep 17 00:00:00 2001 From: James Hogan Date: Thu, 29 May 2014 10:16:24 +0100 Subject: MIPS: Export local_flush_icache_range for KVM Export the local_flush_icache_range function pointer for GPL modules so that it can be used by KVM for syncing the icache after binary translation of trapping instructions. Signed-off-by: James Hogan Cc: Paolo Bonzini Cc: Gleb Natapov Cc: kvm@vger.kernel.org Cc: linux-mips@linux-mips.org Cc: Sanjay Lal Acked-by: Ralf Baechle Signed-off-by: Paolo Bonzini --- arch/mips/mm/cache.c | 1 + 1 file changed, 1 insertion(+) (limited to 'arch/mips') diff --git a/arch/mips/mm/cache.c b/arch/mips/mm/cache.c index e422b38d3113..5aaa5c799731 100644 --- a/arch/mips/mm/cache.c +++ b/arch/mips/mm/cache.c @@ -30,6 +30,7 @@ void (*flush_cache_page)(struct vm_area_struct *vma, unsigned long page, unsigned long pfn); void (*flush_icache_range)(unsigned long start, unsigned long end); void (*local_flush_icache_range)(unsigned long start, unsigned long end); +EXPORT_SYMBOL_GPL(local_flush_icache_range); void (*__flush_cache_vmap)(void); void (*__flush_cache_vunmap)(void); -- cgit v1.2.3 From facaaec1a72db90127b71d22e788596cf1991ae1 Mon Sep 17 00:00:00 2001 From: James Hogan Date: Thu, 29 May 2014 10:16:25 +0100 Subject: MIPS: KVM: Use local_flush_icache_range to fix RI on XBurst MIPS KVM uses mips32_SyncICache to synchronise the icache with the dcache after dynamically modifying guest instructions or writing guest exception vector. However this uses rdhwr to get the SYNCI step, which causes a reserved instruction exception on Ingenic XBurst cores. It would seem to make more sense to use local_flush_icache_range() instead which does the same thing but is more portable. Signed-off-by: James Hogan Cc: Paolo Bonzini Cc: Gleb Natapov Cc: kvm@vger.kernel.org Cc: Ralf Baechle Cc: linux-mips@linux-mips.org Cc: Sanjay Lal Signed-off-by: Paolo Bonzini --- arch/mips/include/asm/kvm_host.h | 1 - arch/mips/kvm/kvm_locore.S | 32 -------------------------------- arch/mips/kvm/kvm_mips.c | 3 ++- arch/mips/kvm/kvm_mips_dyntrans.c | 15 +++++++++------ arch/mips/kvm/kvm_mips_emul.c | 2 +- 5 files changed, 12 insertions(+), 41 deletions(-) (limited to 'arch/mips') diff --git a/arch/mips/include/asm/kvm_host.h b/arch/mips/include/asm/kvm_host.h index 060aaa6348d7..f0e25c6d10b2 100644 --- a/arch/mips/include/asm/kvm_host.h +++ b/arch/mips/include/asm/kvm_host.h @@ -646,7 +646,6 @@ extern int kvm_mips_trans_mtc0(uint32_t inst, uint32_t *opc, struct kvm_vcpu *vcpu); /* Misc */ -extern void mips32_SyncICache(unsigned long addr, unsigned long size); extern int kvm_mips_dump_stats(struct kvm_vcpu *vcpu); extern unsigned long kvm_mips_get_ramsize(struct kvm *kvm); diff --git a/arch/mips/kvm/kvm_locore.S b/arch/mips/kvm/kvm_locore.S index bbace092ad0a..033ac343e72c 100644 --- a/arch/mips/kvm/kvm_locore.S +++ b/arch/mips/kvm/kvm_locore.S @@ -611,35 +611,3 @@ MIPSX(exceptions): .word _C_LABEL(MIPSX(GuestException)) # 29 .word _C_LABEL(MIPSX(GuestException)) # 30 .word _C_LABEL(MIPSX(GuestException)) # 31 - - -/* This routine makes changes to the instruction stream effective to the hardware. - * It should be called after the instruction stream is written. - * On return, the new instructions are effective. - * Inputs: - * a0 = Start address of new instruction stream - * a1 = Size, in bytes, of new instruction stream - */ - -#define HW_SYNCI_Step $1 -LEAF(MIPSX(SyncICache)) - .set push - .set mips32r2 - beq a1, zero, 20f - nop - REG_ADDU a1, a0, a1 - rdhwr v0, HW_SYNCI_Step - beq v0, zero, 20f - nop -10: - synci 0(a0) - REG_ADDU a0, a0, v0 - sltu v1, a0, a1 - bne v1, zero, 10b - nop - sync -20: - jr.hb ra - nop - .set pop -END(MIPSX(SyncICache)) diff --git a/arch/mips/kvm/kvm_mips.c b/arch/mips/kvm/kvm_mips.c index 1e12762ae7ea..2a80a906ccc7 100644 --- a/arch/mips/kvm/kvm_mips.c +++ b/arch/mips/kvm/kvm_mips.c @@ -345,7 +345,8 @@ struct kvm_vcpu *kvm_arch_vcpu_create(struct kvm *kvm, unsigned int id) mips32_GuestExceptionEnd - mips32_GuestException); /* Invalidate the icache for these ranges */ - mips32_SyncICache((unsigned long) gebase, ALIGN(size, PAGE_SIZE)); + local_flush_icache_range((unsigned long)gebase, + (unsigned long)gebase + ALIGN(size, PAGE_SIZE)); /* Allocate comm page for guest kernel, a TLB will be reserved for mapping GVA @ 0xFFFF8000 to this page */ vcpu->arch.kseg0_commpage = kzalloc(PAGE_SIZE << 1, GFP_KERNEL); diff --git a/arch/mips/kvm/kvm_mips_dyntrans.c b/arch/mips/kvm/kvm_mips_dyntrans.c index 96528e2d1ea6..b80e41d858fd 100644 --- a/arch/mips/kvm/kvm_mips_dyntrans.c +++ b/arch/mips/kvm/kvm_mips_dyntrans.c @@ -16,6 +16,7 @@ #include #include #include +#include #include "kvm_mips_comm.h" @@ -40,7 +41,7 @@ kvm_mips_trans_cache_index(uint32_t inst, uint32_t *opc, CKSEG0ADDR(kvm_mips_translate_guest_kseg0_to_hpa (vcpu, (unsigned long) opc)); memcpy((void *)kseg0_opc, (void *)&synci_inst, sizeof(uint32_t)); - mips32_SyncICache(kseg0_opc, 32); + local_flush_icache_range(kseg0_opc, kseg0_opc + 32); return result; } @@ -66,7 +67,7 @@ kvm_mips_trans_cache_va(uint32_t inst, uint32_t *opc, CKSEG0ADDR(kvm_mips_translate_guest_kseg0_to_hpa (vcpu, (unsigned long) opc)); memcpy((void *)kseg0_opc, (void *)&synci_inst, sizeof(uint32_t)); - mips32_SyncICache(kseg0_opc, 32); + local_flush_icache_range(kseg0_opc, kseg0_opc + 32); return result; } @@ -99,11 +100,12 @@ kvm_mips_trans_mfc0(uint32_t inst, uint32_t *opc, struct kvm_vcpu *vcpu) CKSEG0ADDR(kvm_mips_translate_guest_kseg0_to_hpa (vcpu, (unsigned long) opc)); memcpy((void *)kseg0_opc, (void *)&mfc0_inst, sizeof(uint32_t)); - mips32_SyncICache(kseg0_opc, 32); + local_flush_icache_range(kseg0_opc, kseg0_opc + 32); } else if (KVM_GUEST_KSEGX((unsigned long) opc) == KVM_GUEST_KSEG23) { local_irq_save(flags); memcpy((void *)opc, (void *)&mfc0_inst, sizeof(uint32_t)); - mips32_SyncICache((unsigned long) opc, 32); + local_flush_icache_range((unsigned long)opc, + (unsigned long)opc + 32); local_irq_restore(flags); } else { kvm_err("%s: Invalid address: %p\n", __func__, opc); @@ -134,11 +136,12 @@ kvm_mips_trans_mtc0(uint32_t inst, uint32_t *opc, struct kvm_vcpu *vcpu) CKSEG0ADDR(kvm_mips_translate_guest_kseg0_to_hpa (vcpu, (unsigned long) opc)); memcpy((void *)kseg0_opc, (void *)&mtc0_inst, sizeof(uint32_t)); - mips32_SyncICache(kseg0_opc, 32); + local_flush_icache_range(kseg0_opc, kseg0_opc + 32); } else if (KVM_GUEST_KSEGX((unsigned long) opc) == KVM_GUEST_KSEG23) { local_irq_save(flags); memcpy((void *)opc, (void *)&mtc0_inst, sizeof(uint32_t)); - mips32_SyncICache((unsigned long) opc, 32); + local_flush_icache_range((unsigned long)opc, + (unsigned long)opc + 32); local_irq_restore(flags); } else { kvm_err("%s: Invalid address: %p\n", __func__, opc); diff --git a/arch/mips/kvm/kvm_mips_emul.c b/arch/mips/kvm/kvm_mips_emul.c index e3fec99941a7..bad31c6235d4 100644 --- a/arch/mips/kvm/kvm_mips_emul.c +++ b/arch/mips/kvm/kvm_mips_emul.c @@ -887,7 +887,7 @@ int kvm_mips_sync_icache(unsigned long va, struct kvm_vcpu *vcpu) printk("%s: va: %#lx, unmapped: %#x\n", __func__, va, CKSEG0ADDR(pa)); - mips32_SyncICache(CKSEG0ADDR(pa), 32); + local_flush_icache_range(CKSEG0ADDR(pa), 32); return 0; } -- cgit v1.2.3 From b5dfc6c10608fdf0c35a747a0a3bf3f6e40a38e1 Mon Sep 17 00:00:00 2001 From: James Hogan Date: Thu, 29 May 2014 10:16:26 +0100 Subject: MIPS: KVM: Use tlb_write_random When MIPS KVM needs to write a TLB entry for the guest it reads the CP0_Random register, uses it to generate the CP_Index, and writes the TLB entry using the TLBWI instruction (tlb_write_indexed()). However there's an instruction for that, TLBWR (tlb_write_random()) so use that instead. This happens to also fix an issue with Ingenic XBurst cores where the same TLB entry is replaced each time preventing forward progress on stores due to alternating between TLB load misses for the instruction fetch and TLB store misses. Signed-off-by: James Hogan Cc: Paolo Bonzini Cc: Gleb Natapov Cc: kvm@vger.kernel.org Cc: Ralf Baechle Cc: linux-mips@linux-mips.org Cc: Sanjay Lal Signed-off-by: Paolo Bonzini --- arch/mips/kvm/kvm_tlb.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'arch/mips') diff --git a/arch/mips/kvm/kvm_tlb.c b/arch/mips/kvm/kvm_tlb.c index 50ab9c4d4a5d..9d371ee0a755 100644 --- a/arch/mips/kvm/kvm_tlb.c +++ b/arch/mips/kvm/kvm_tlb.c @@ -222,16 +222,14 @@ kvm_mips_host_tlb_write(struct kvm_vcpu *vcpu, unsigned long entryhi, return -1; } - if (idx < 0) { - idx = read_c0_random() % current_cpu_data.tlbsize; - write_c0_index(idx); - mtc0_tlbw_hazard(); - } write_c0_entrylo0(entrylo0); write_c0_entrylo1(entrylo1); mtc0_tlbw_hazard(); - tlb_write_indexed(); + if (idx < 0) + tlb_write_random(); + else + tlb_write_indexed(); tlbw_use_hazard(); #ifdef DEBUG -- cgit v1.2.3 From fb6df0cdf0fe6fcb28fe29b965d9141bceaf00a7 Mon Sep 17 00:00:00 2001 From: James Hogan Date: Thu, 29 May 2014 10:16:27 +0100 Subject: MIPS: KVM: Add CP0_EPC KVM register access Contrary to the comment, the guest CP0_EPC register cannot be set via kvm_regs, since it is distinct from the guest PC. Add the EPC register to the KVM_{GET,SET}_ONE_REG ioctl interface. Signed-off-by: James Hogan Cc: Paolo Bonzini Cc: Gleb Natapov Cc: kvm@vger.kernel.org Cc: Ralf Baechle Cc: linux-mips@linux-mips.org Cc: David Daney Cc: Sanjay Lal Signed-off-by: Paolo Bonzini --- arch/mips/kvm/kvm_mips.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'arch/mips') diff --git a/arch/mips/kvm/kvm_mips.c b/arch/mips/kvm/kvm_mips.c index 2a80a906ccc7..6b890ebe0e04 100644 --- a/arch/mips/kvm/kvm_mips.c +++ b/arch/mips/kvm/kvm_mips.c @@ -507,6 +507,7 @@ kvm_arch_vcpu_ioctl_set_mpstate(struct kvm_vcpu *vcpu, #define KVM_REG_MIPS_CP0_COMPARE MIPS_CP0_32(11, 0) #define KVM_REG_MIPS_CP0_STATUS MIPS_CP0_32(12, 0) #define KVM_REG_MIPS_CP0_CAUSE MIPS_CP0_32(13, 0) +#define KVM_REG_MIPS_CP0_EPC MIPS_CP0_64(14, 0) #define KVM_REG_MIPS_CP0_EBASE MIPS_CP0_64(15, 1) #define KVM_REG_MIPS_CP0_CONFIG MIPS_CP0_32(16, 0) #define KVM_REG_MIPS_CP0_CONFIG1 MIPS_CP0_32(16, 1) @@ -562,7 +563,7 @@ static u64 kvm_mips_get_one_regs[] = { KVM_REG_MIPS_CP0_ENTRYHI, KVM_REG_MIPS_CP0_STATUS, KVM_REG_MIPS_CP0_CAUSE, - /* EPC set via kvm_regs, et al. */ + KVM_REG_MIPS_CP0_EPC, KVM_REG_MIPS_CP0_CONFIG, KVM_REG_MIPS_CP0_CONFIG1, KVM_REG_MIPS_CP0_CONFIG2, @@ -615,6 +616,9 @@ static int kvm_mips_get_reg(struct kvm_vcpu *vcpu, case KVM_REG_MIPS_CP0_CAUSE: v = (long)kvm_read_c0_guest_cause(cop0); break; + case KVM_REG_MIPS_CP0_EPC: + v = (long)kvm_read_c0_guest_epc(cop0); + break; case KVM_REG_MIPS_CP0_ERROREPC: v = (long)kvm_read_c0_guest_errorepc(cop0); break; @@ -711,6 +715,9 @@ static int kvm_mips_set_reg(struct kvm_vcpu *vcpu, case KVM_REG_MIPS_CP0_CAUSE: kvm_write_c0_guest_cause(cop0, v); break; + case KVM_REG_MIPS_CP0_EPC: + kvm_write_c0_guest_epc(cop0, v); + break; case KVM_REG_MIPS_CP0_ERROREPC: kvm_write_c0_guest_errorepc(cop0, v); break; -- cgit v1.2.3 From 48a3c4e4cd6aa1a2270f3dd2a29e44206e4b2428 Mon Sep 17 00:00:00 2001 From: James Hogan Date: Thu, 29 May 2014 10:16:28 +0100 Subject: MIPS: KVM: Move KVM_{GET,SET}_ONE_REG definitions into kvm_host.h Move the KVM_{GET,SET}_ONE_REG MIPS register id definitions out of kvm_mips.c to kvm_host.h so that they can be shared between multiple source files. This allows register access to be indirected depending on the underlying implementation (trap & emulate or VZ). Signed-off-by: James Hogan Cc: Paolo Bonzini Cc: Gleb Natapov Cc: kvm@vger.kernel.org Cc: Ralf Baechle Cc: linux-mips@linux-mips.org Cc: David Daney Cc: Sanjay Lal Signed-off-by: Paolo Bonzini --- arch/mips/include/asm/kvm_host.h | 32 ++++++++++++++++++++++++++++++++ arch/mips/kvm/kvm_mips.c | 31 ------------------------------- 2 files changed, 32 insertions(+), 31 deletions(-) (limited to 'arch/mips') diff --git a/arch/mips/include/asm/kvm_host.h b/arch/mips/include/asm/kvm_host.h index f0e25c6d10b2..9f6bfc963a5b 100644 --- a/arch/mips/include/asm/kvm_host.h +++ b/arch/mips/include/asm/kvm_host.h @@ -19,6 +19,38 @@ #include #include +/* MIPS KVM register ids */ +#define MIPS_CP0_32(_R, _S) \ + (KVM_REG_MIPS | KVM_REG_SIZE_U32 | 0x10000 | (8 * (_R) + (_S))) + +#define MIPS_CP0_64(_R, _S) \ + (KVM_REG_MIPS | KVM_REG_SIZE_U64 | 0x10000 | (8 * (_R) + (_S))) + +#define KVM_REG_MIPS_CP0_INDEX MIPS_CP0_32(0, 0) +#define KVM_REG_MIPS_CP0_ENTRYLO0 MIPS_CP0_64(2, 0) +#define KVM_REG_MIPS_CP0_ENTRYLO1 MIPS_CP0_64(3, 0) +#define KVM_REG_MIPS_CP0_CONTEXT MIPS_CP0_64(4, 0) +#define KVM_REG_MIPS_CP0_USERLOCAL MIPS_CP0_64(4, 2) +#define KVM_REG_MIPS_CP0_PAGEMASK MIPS_CP0_32(5, 0) +#define KVM_REG_MIPS_CP0_PAGEGRAIN MIPS_CP0_32(5, 1) +#define KVM_REG_MIPS_CP0_WIRED MIPS_CP0_32(6, 0) +#define KVM_REG_MIPS_CP0_HWRENA MIPS_CP0_32(7, 0) +#define KVM_REG_MIPS_CP0_BADVADDR MIPS_CP0_64(8, 0) +#define KVM_REG_MIPS_CP0_COUNT MIPS_CP0_32(9, 0) +#define KVM_REG_MIPS_CP0_ENTRYHI MIPS_CP0_64(10, 0) +#define KVM_REG_MIPS_CP0_COMPARE MIPS_CP0_32(11, 0) +#define KVM_REG_MIPS_CP0_STATUS MIPS_CP0_32(12, 0) +#define KVM_REG_MIPS_CP0_CAUSE MIPS_CP0_32(13, 0) +#define KVM_REG_MIPS_CP0_EPC MIPS_CP0_64(14, 0) +#define KVM_REG_MIPS_CP0_EBASE MIPS_CP0_64(15, 1) +#define KVM_REG_MIPS_CP0_CONFIG MIPS_CP0_32(16, 0) +#define KVM_REG_MIPS_CP0_CONFIG1 MIPS_CP0_32(16, 1) +#define KVM_REG_MIPS_CP0_CONFIG2 MIPS_CP0_32(16, 2) +#define KVM_REG_MIPS_CP0_CONFIG3 MIPS_CP0_32(16, 3) +#define KVM_REG_MIPS_CP0_CONFIG7 MIPS_CP0_32(16, 7) +#define KVM_REG_MIPS_CP0_XCONTEXT MIPS_CP0_64(20, 0) +#define KVM_REG_MIPS_CP0_ERROREPC MIPS_CP0_64(30, 0) + #define KVM_MAX_VCPUS 1 #define KVM_USER_MEM_SLOTS 8 diff --git a/arch/mips/kvm/kvm_mips.c b/arch/mips/kvm/kvm_mips.c index 6b890ebe0e04..3f52d72c19f1 100644 --- a/arch/mips/kvm/kvm_mips.c +++ b/arch/mips/kvm/kvm_mips.c @@ -486,37 +486,6 @@ kvm_arch_vcpu_ioctl_set_mpstate(struct kvm_vcpu *vcpu, return -ENOIOCTLCMD; } -#define MIPS_CP0_32(_R, _S) \ - (KVM_REG_MIPS | KVM_REG_SIZE_U32 | 0x10000 | (8 * (_R) + (_S))) - -#define MIPS_CP0_64(_R, _S) \ - (KVM_REG_MIPS | KVM_REG_SIZE_U64 | 0x10000 | (8 * (_R) + (_S))) - -#define KVM_REG_MIPS_CP0_INDEX MIPS_CP0_32(0, 0) -#define KVM_REG_MIPS_CP0_ENTRYLO0 MIPS_CP0_64(2, 0) -#define KVM_REG_MIPS_CP0_ENTRYLO1 MIPS_CP0_64(3, 0) -#define KVM_REG_MIPS_CP0_CONTEXT MIPS_CP0_64(4, 0) -#define KVM_REG_MIPS_CP0_USERLOCAL MIPS_CP0_64(4, 2) -#define KVM_REG_MIPS_CP0_PAGEMASK MIPS_CP0_32(5, 0) -#define KVM_REG_MIPS_CP0_PAGEGRAIN MIPS_CP0_32(5, 1) -#define KVM_REG_MIPS_CP0_WIRED MIPS_CP0_32(6, 0) -#define KVM_REG_MIPS_CP0_HWRENA MIPS_CP0_32(7, 0) -#define KVM_REG_MIPS_CP0_BADVADDR MIPS_CP0_64(8, 0) -#define KVM_REG_MIPS_CP0_COUNT MIPS_CP0_32(9, 0) -#define KVM_REG_MIPS_CP0_ENTRYHI MIPS_CP0_64(10, 0) -#define KVM_REG_MIPS_CP0_COMPARE MIPS_CP0_32(11, 0) -#define KVM_REG_MIPS_CP0_STATUS MIPS_CP0_32(12, 0) -#define KVM_REG_MIPS_CP0_CAUSE MIPS_CP0_32(13, 0) -#define KVM_REG_MIPS_CP0_EPC MIPS_CP0_64(14, 0) -#define KVM_REG_MIPS_CP0_EBASE MIPS_CP0_64(15, 1) -#define KVM_REG_MIPS_CP0_CONFIG MIPS_CP0_32(16, 0) -#define KVM_REG_MIPS_CP0_CONFIG1 MIPS_CP0_32(16, 1) -#define KVM_REG_MIPS_CP0_CONFIG2 MIPS_CP0_32(16, 2) -#define KVM_REG_MIPS_CP0_CONFIG3 MIPS_CP0_32(16, 3) -#define KVM_REG_MIPS_CP0_CONFIG7 MIPS_CP0_32(16, 7) -#define KVM_REG_MIPS_CP0_XCONTEXT MIPS_CP0_64(20, 0) -#define KVM_REG_MIPS_CP0_ERROREPC MIPS_CP0_64(30, 0) - static u64 kvm_mips_get_one_regs[] = { KVM_REG_MIPS_R0, KVM_REG_MIPS_R1, -- cgit v1.2.3 From f8be02dacaca49ac442d03a178d4290a083c3922 Mon Sep 17 00:00:00 2001 From: James Hogan Date: Thu, 29 May 2014 10:16:29 +0100 Subject: MIPS: KVM: Add CP0_Count/Compare KVM register access Implement KVM_{GET,SET}_ONE_REG ioctl based access to the guest CP0 Count and Compare registers. These registers are special in that writing to them has side effects (adjusting the time until the next timer interrupt) and reading of Count depends on the time. Therefore add a couple of callbacks so that different implementations (trap & emulate or VZ) can implement them differently depending on what the hardware provides. The trap & emulate versions mostly duplicate what happens when a T&E guest reads or writes these registers, so it inherits the same limitations which can be fixed in later patches. Signed-off-by: James Hogan Cc: Paolo Bonzini Cc: Gleb Natapov Cc: kvm@vger.kernel.org Cc: Ralf Baechle Cc: linux-mips@linux-mips.org Cc: David Daney Cc: Sanjay Lal Signed-off-by: Paolo Bonzini --- arch/mips/include/asm/kvm_host.h | 4 ++++ arch/mips/kvm/kvm_mips.c | 16 ++++++++++++++++ arch/mips/kvm/kvm_trap_emul.c | 36 ++++++++++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+) (limited to 'arch/mips') diff --git a/arch/mips/include/asm/kvm_host.h b/arch/mips/include/asm/kvm_host.h index 9f6bfc963a5b..41e180ed36e3 100644 --- a/arch/mips/include/asm/kvm_host.h +++ b/arch/mips/include/asm/kvm_host.h @@ -523,6 +523,10 @@ struct kvm_mips_callbacks { uint32_t cause); int (*irq_clear) (struct kvm_vcpu *vcpu, unsigned int priority, uint32_t cause); + int (*get_one_reg)(struct kvm_vcpu *vcpu, + const struct kvm_one_reg *reg, s64 *v); + int (*set_one_reg)(struct kvm_vcpu *vcpu, + const struct kvm_one_reg *reg, s64 v); }; extern struct kvm_mips_callbacks *kvm_mips_callbacks; int kvm_mips_emulation_init(struct kvm_mips_callbacks **install_callbacks); diff --git a/arch/mips/kvm/kvm_mips.c b/arch/mips/kvm/kvm_mips.c index 3f52d72c19f1..61d834cc35cf 100644 --- a/arch/mips/kvm/kvm_mips.c +++ b/arch/mips/kvm/kvm_mips.c @@ -529,7 +529,9 @@ static u64 kvm_mips_get_one_regs[] = { KVM_REG_MIPS_CP0_PAGEMASK, KVM_REG_MIPS_CP0_WIRED, KVM_REG_MIPS_CP0_BADVADDR, + KVM_REG_MIPS_CP0_COUNT, KVM_REG_MIPS_CP0_ENTRYHI, + KVM_REG_MIPS_CP0_COMPARE, KVM_REG_MIPS_CP0_STATUS, KVM_REG_MIPS_CP0_CAUSE, KVM_REG_MIPS_CP0_EPC, @@ -545,6 +547,7 @@ static int kvm_mips_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg) { struct mips_coproc *cop0 = vcpu->arch.cop0; + int ret; s64 v; switch (reg->id) { @@ -579,6 +582,9 @@ static int kvm_mips_get_reg(struct kvm_vcpu *vcpu, case KVM_REG_MIPS_CP0_ENTRYHI: v = (long)kvm_read_c0_guest_entryhi(cop0); break; + case KVM_REG_MIPS_CP0_COMPARE: + v = (long)kvm_read_c0_guest_compare(cop0); + break; case KVM_REG_MIPS_CP0_STATUS: v = (long)kvm_read_c0_guest_status(cop0); break; @@ -606,6 +612,12 @@ static int kvm_mips_get_reg(struct kvm_vcpu *vcpu, case KVM_REG_MIPS_CP0_CONFIG7: v = (long)kvm_read_c0_guest_config7(cop0); break; + /* registers to be handled specially */ + case KVM_REG_MIPS_CP0_COUNT: + ret = kvm_mips_callbacks->get_one_reg(vcpu, reg, &v); + if (ret) + return ret; + break; default: return -EINVAL; } @@ -690,6 +702,10 @@ static int kvm_mips_set_reg(struct kvm_vcpu *vcpu, case KVM_REG_MIPS_CP0_ERROREPC: kvm_write_c0_guest_errorepc(cop0, v); break; + /* registers to be handled specially */ + case KVM_REG_MIPS_CP0_COUNT: + case KVM_REG_MIPS_CP0_COMPARE: + return kvm_mips_callbacks->set_one_reg(vcpu, reg, v); default: return -EINVAL; } diff --git a/arch/mips/kvm/kvm_trap_emul.c b/arch/mips/kvm/kvm_trap_emul.c index 30d725321db1..f1e8389f8d33 100644 --- a/arch/mips/kvm/kvm_trap_emul.c +++ b/arch/mips/kvm/kvm_trap_emul.c @@ -401,6 +401,40 @@ static int kvm_trap_emul_vcpu_setup(struct kvm_vcpu *vcpu) return 0; } +static int kvm_trap_emul_get_one_reg(struct kvm_vcpu *vcpu, + const struct kvm_one_reg *reg, + s64 *v) +{ + switch (reg->id) { + case KVM_REG_MIPS_CP0_COUNT: + /* XXXKYMA: Run the Guest count register @ 1/4 the rate of the host */ + *v = (read_c0_count() >> 2); + break; + default: + return -EINVAL; + } + return 0; +} + +static int kvm_trap_emul_set_one_reg(struct kvm_vcpu *vcpu, + const struct kvm_one_reg *reg, + s64 v) +{ + struct mips_coproc *cop0 = vcpu->arch.cop0; + + switch (reg->id) { + case KVM_REG_MIPS_CP0_COUNT: + /* Not supported yet */ + break; + case KVM_REG_MIPS_CP0_COMPARE: + kvm_write_c0_guest_compare(cop0, v); + break; + default: + return -EINVAL; + } + return 0; +} + static struct kvm_mips_callbacks kvm_trap_emul_callbacks = { /* exit handlers */ .handle_cop_unusable = kvm_trap_emul_handle_cop_unusable, @@ -423,6 +457,8 @@ static struct kvm_mips_callbacks kvm_trap_emul_callbacks = { .dequeue_io_int = kvm_mips_dequeue_io_int_cb, .irq_deliver = kvm_mips_irq_deliver_cb, .irq_clear = kvm_mips_irq_clear_cb, + .get_one_reg = kvm_trap_emul_get_one_reg, + .set_one_reg = kvm_trap_emul_set_one_reg, }; int kvm_mips_emulation_init(struct kvm_mips_callbacks **install_callbacks) -- cgit v1.2.3 From 7767b7d2f7c4d81f8c6710af1f5cb3fc038fa7be Mon Sep 17 00:00:00 2001 From: James Hogan Date: Thu, 29 May 2014 10:16:30 +0100 Subject: MIPS: KVM: Add CP0_UserLocal KVM register access Implement KVM_{GET,SET}_ONE_REG ioctl based access to the guest CP0 UserLocal register. This is so that userland can save and restore its value. Signed-off-by: James Hogan Cc: Paolo Bonzini Cc: Gleb Natapov Cc: kvm@vger.kernel.org Cc: Ralf Baechle Cc: linux-mips@linux-mips.org Cc: David Daney Cc: Sanjay Lal Signed-off-by: Paolo Bonzini --- arch/mips/include/asm/kvm_host.h | 1 + arch/mips/kvm/kvm_mips.c | 7 +++++++ 2 files changed, 8 insertions(+) (limited to 'arch/mips') diff --git a/arch/mips/include/asm/kvm_host.h b/arch/mips/include/asm/kvm_host.h index 41e180ed36e3..6f9338450e24 100644 --- a/arch/mips/include/asm/kvm_host.h +++ b/arch/mips/include/asm/kvm_host.h @@ -442,6 +442,7 @@ struct kvm_vcpu_arch { #define kvm_read_c0_guest_context(cop0) (cop0->reg[MIPS_CP0_TLB_CONTEXT][0]) #define kvm_write_c0_guest_context(cop0, val) (cop0->reg[MIPS_CP0_TLB_CONTEXT][0] = (val)) #define kvm_read_c0_guest_userlocal(cop0) (cop0->reg[MIPS_CP0_TLB_CONTEXT][2]) +#define kvm_write_c0_guest_userlocal(cop0, val) (cop0->reg[MIPS_CP0_TLB_CONTEXT][2] = (val)) #define kvm_read_c0_guest_pagemask(cop0) (cop0->reg[MIPS_CP0_TLB_PG_MASK][0]) #define kvm_write_c0_guest_pagemask(cop0, val) (cop0->reg[MIPS_CP0_TLB_PG_MASK][0] = (val)) #define kvm_read_c0_guest_wired(cop0) (cop0->reg[MIPS_CP0_TLB_WIRED][0]) diff --git a/arch/mips/kvm/kvm_mips.c b/arch/mips/kvm/kvm_mips.c index 61d834cc35cf..26f10fb8dcba 100644 --- a/arch/mips/kvm/kvm_mips.c +++ b/arch/mips/kvm/kvm_mips.c @@ -526,6 +526,7 @@ static u64 kvm_mips_get_one_regs[] = { KVM_REG_MIPS_CP0_INDEX, KVM_REG_MIPS_CP0_CONTEXT, + KVM_REG_MIPS_CP0_USERLOCAL, KVM_REG_MIPS_CP0_PAGEMASK, KVM_REG_MIPS_CP0_WIRED, KVM_REG_MIPS_CP0_BADVADDR, @@ -570,6 +571,9 @@ static int kvm_mips_get_reg(struct kvm_vcpu *vcpu, case KVM_REG_MIPS_CP0_CONTEXT: v = (long)kvm_read_c0_guest_context(cop0); break; + case KVM_REG_MIPS_CP0_USERLOCAL: + v = (long)kvm_read_c0_guest_userlocal(cop0); + break; case KVM_REG_MIPS_CP0_PAGEMASK: v = (long)kvm_read_c0_guest_pagemask(cop0); break; @@ -678,6 +682,9 @@ static int kvm_mips_set_reg(struct kvm_vcpu *vcpu, case KVM_REG_MIPS_CP0_CONTEXT: kvm_write_c0_guest_context(cop0, v); break; + case KVM_REG_MIPS_CP0_USERLOCAL: + kvm_write_c0_guest_userlocal(cop0, v); + break; case KVM_REG_MIPS_CP0_PAGEMASK: kvm_write_c0_guest_pagemask(cop0, v); break; -- cgit v1.2.3 From 16fd5c1de4f00c7887e4c715dd46872a97e30e25 Mon Sep 17 00:00:00 2001 From: James Hogan Date: Thu, 29 May 2014 10:16:31 +0100 Subject: MIPS: KVM: Add CP0_HWREna KVM register access Implement KVM_{GET,SET}_ONE_REG ioctl based access to the guest CP0 HWREna register. This is so that userland can save and restore its value so that RDHWR instructions don't have to be emulated by the guest. Signed-off-by: James Hogan Cc: Paolo Bonzini Cc: Gleb Natapov Cc: kvm@vger.kernel.org Cc: Ralf Baechle Cc: linux-mips@linux-mips.org Cc: David Daney Cc: Sanjay Lal Signed-off-by: Paolo Bonzini --- arch/mips/kvm/kvm_mips.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'arch/mips') diff --git a/arch/mips/kvm/kvm_mips.c b/arch/mips/kvm/kvm_mips.c index 26f10fb8dcba..e4cb7cff9a28 100644 --- a/arch/mips/kvm/kvm_mips.c +++ b/arch/mips/kvm/kvm_mips.c @@ -529,6 +529,7 @@ static u64 kvm_mips_get_one_regs[] = { KVM_REG_MIPS_CP0_USERLOCAL, KVM_REG_MIPS_CP0_PAGEMASK, KVM_REG_MIPS_CP0_WIRED, + KVM_REG_MIPS_CP0_HWRENA, KVM_REG_MIPS_CP0_BADVADDR, KVM_REG_MIPS_CP0_COUNT, KVM_REG_MIPS_CP0_ENTRYHI, @@ -580,6 +581,9 @@ static int kvm_mips_get_reg(struct kvm_vcpu *vcpu, case KVM_REG_MIPS_CP0_WIRED: v = (long)kvm_read_c0_guest_wired(cop0); break; + case KVM_REG_MIPS_CP0_HWRENA: + v = (long)kvm_read_c0_guest_hwrena(cop0); + break; case KVM_REG_MIPS_CP0_BADVADDR: v = (long)kvm_read_c0_guest_badvaddr(cop0); break; @@ -691,6 +695,9 @@ static int kvm_mips_set_reg(struct kvm_vcpu *vcpu, case KVM_REG_MIPS_CP0_WIRED: kvm_write_c0_guest_wired(cop0, v); break; + case KVM_REG_MIPS_CP0_HWRENA: + kvm_write_c0_guest_hwrena(cop0, v); + break; case KVM_REG_MIPS_CP0_BADVADDR: kvm_write_c0_guest_badvaddr(cop0, v); break; -- cgit v1.2.3 From 044f0f03eca0110e1835b2ea038a484b93950328 Mon Sep 17 00:00:00 2001 From: James Hogan Date: Thu, 29 May 2014 10:16:32 +0100 Subject: MIPS: KVM: Deliver guest interrupts after local_irq_disable() When about to run the guest, deliver guest interrupts after disabling host interrupts. This should prevent an hrtimer interrupt from being handled after delivering guest interrupts, and therefore not delivering the guest timer interrupt until after the next guest exit. Signed-off-by: James Hogan Cc: Paolo Bonzini Cc: Gleb Natapov Cc: kvm@vger.kernel.org Cc: Ralf Baechle Cc: linux-mips@linux-mips.org Cc: Sanjay Lal Signed-off-by: Paolo Bonzini --- arch/mips/kvm/kvm_mips.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'arch/mips') diff --git a/arch/mips/kvm/kvm_mips.c b/arch/mips/kvm/kvm_mips.c index e4cb7cff9a28..0a9c7ab56df1 100644 --- a/arch/mips/kvm/kvm_mips.c +++ b/arch/mips/kvm/kvm_mips.c @@ -419,11 +419,11 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run) vcpu->mmio_needed = 0; } + local_irq_disable(); /* Check if we have any exceptions/interrupts pending */ kvm_mips_deliver_interrupts(vcpu, kvm_read_c0_guest_cause(vcpu->arch.cop0)); - local_irq_disable(); kvm_guest_enter(); r = __kvm_mips_vcpu_run(run, vcpu); -- cgit v1.2.3 From c73c99b0dfa7cab4100fb2699d0b7362322278a1 Mon Sep 17 00:00:00 2001 From: James Hogan Date: Thu, 29 May 2014 10:16:33 +0100 Subject: MIPS: KVM: Fix timer race modifying guest CP0_Cause The hrtimer callback for guest timer timeouts sets the guest's CP0_Cause.TI bit to indicate to the guest that a timer interrupt is pending, however there is no mutual exclusion implemented to prevent this occurring while the guest's CP0_Cause register is being read-modify-written elsewhere. When this occurs the setting of the CP0_Cause.TI bit is undone and the guest misses the timer interrupt and doesn't reprogram the CP0_Compare register for the next timeout. Currently another timer interrupt will be triggered again in another 10ms anyway due to the way timers are emulated, but after the MIPS timer emulation is fixed this would result in Linux guest time standing still and the guest scheduler not being invoked until the guest CP0_Count has looped around again, which at 100MHz takes just under 43 seconds. Currently this is the only asynchronous modification of guest registers, therefore it is fixed by adjusting the implementations of the kvm_set_c0_guest_cause(), kvm_clear_c0_guest_cause(), and kvm_change_c0_guest_cause() macros which are used for modifying the guest CP0_Cause register to use ll/sc to ensure atomic modification. This should work in both UP and SMP cases without requiring interrupts to be disabled. Signed-off-by: James Hogan Cc: Paolo Bonzini Cc: Gleb Natapov Cc: kvm@vger.kernel.org Cc: Ralf Baechle Cc: linux-mips@linux-mips.org Cc: Sanjay Lal Signed-off-by: Paolo Bonzini --- arch/mips/include/asm/kvm_host.h | 71 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 65 insertions(+), 6 deletions(-) (limited to 'arch/mips') diff --git a/arch/mips/include/asm/kvm_host.h b/arch/mips/include/asm/kvm_host.h index 6f9338450e24..79410f85a5a7 100644 --- a/arch/mips/include/asm/kvm_host.h +++ b/arch/mips/include/asm/kvm_host.h @@ -482,15 +482,74 @@ struct kvm_vcpu_arch { #define kvm_read_c0_guest_errorepc(cop0) (cop0->reg[MIPS_CP0_ERROR_PC][0]) #define kvm_write_c0_guest_errorepc(cop0, val) (cop0->reg[MIPS_CP0_ERROR_PC][0] = (val)) +/* + * Some of the guest registers may be modified asynchronously (e.g. from a + * hrtimer callback in hard irq context) and therefore need stronger atomicity + * guarantees than other registers. + */ + +static inline void _kvm_atomic_set_c0_guest_reg(unsigned long *reg, + unsigned long val) +{ + unsigned long temp; + do { + __asm__ __volatile__( + " .set mips3 \n" + " " __LL "%0, %1 \n" + " or %0, %2 \n" + " " __SC "%0, %1 \n" + " .set mips0 \n" + : "=&r" (temp), "+m" (*reg) + : "r" (val)); + } while (unlikely(!temp)); +} + +static inline void _kvm_atomic_clear_c0_guest_reg(unsigned long *reg, + unsigned long val) +{ + unsigned long temp; + do { + __asm__ __volatile__( + " .set mips3 \n" + " " __LL "%0, %1 \n" + " and %0, %2 \n" + " " __SC "%0, %1 \n" + " .set mips0 \n" + : "=&r" (temp), "+m" (*reg) + : "r" (~val)); + } while (unlikely(!temp)); +} + +static inline void _kvm_atomic_change_c0_guest_reg(unsigned long *reg, + unsigned long change, + unsigned long val) +{ + unsigned long temp; + do { + __asm__ __volatile__( + " .set mips3 \n" + " " __LL "%0, %1 \n" + " and %0, %2 \n" + " or %0, %3 \n" + " " __SC "%0, %1 \n" + " .set mips0 \n" + : "=&r" (temp), "+m" (*reg) + : "r" (~change), "r" (val & change)); + } while (unlikely(!temp)); +} + #define kvm_set_c0_guest_status(cop0, val) (cop0->reg[MIPS_CP0_STATUS][0] |= (val)) #define kvm_clear_c0_guest_status(cop0, val) (cop0->reg[MIPS_CP0_STATUS][0] &= ~(val)) -#define kvm_set_c0_guest_cause(cop0, val) (cop0->reg[MIPS_CP0_CAUSE][0] |= (val)) -#define kvm_clear_c0_guest_cause(cop0, val) (cop0->reg[MIPS_CP0_CAUSE][0] &= ~(val)) + +/* Cause can be modified asynchronously from hardirq hrtimer callback */ +#define kvm_set_c0_guest_cause(cop0, val) \ + _kvm_atomic_set_c0_guest_reg(&cop0->reg[MIPS_CP0_CAUSE][0], val) +#define kvm_clear_c0_guest_cause(cop0, val) \ + _kvm_atomic_clear_c0_guest_reg(&cop0->reg[MIPS_CP0_CAUSE][0], val) #define kvm_change_c0_guest_cause(cop0, change, val) \ -{ \ - kvm_clear_c0_guest_cause(cop0, change); \ - kvm_set_c0_guest_cause(cop0, ((val) & (change))); \ -} + _kvm_atomic_change_c0_guest_reg(&cop0->reg[MIPS_CP0_CAUSE][0], \ + change, val) + #define kvm_set_c0_guest_ebase(cop0, val) (cop0->reg[MIPS_CP0_PRID][1] |= (val)) #define kvm_clear_c0_guest_ebase(cop0, val) (cop0->reg[MIPS_CP0_PRID][1] &= ~(val)) #define kvm_change_c0_guest_ebase(cop0, change, val) \ -- cgit v1.2.3 From 3a0ba77408f824b1cebf5134c710a8455d7bc8f4 Mon Sep 17 00:00:00 2001 From: James Hogan Date: Thu, 29 May 2014 10:16:34 +0100 Subject: MIPS: KVM: Migrate hrtimer to follow VCPU When a VCPU is scheduled in on a different CPU, refresh the hrtimer used for emulating count/compare so that it gets migrated to the same CPU. This should prevent a timer interrupt occurring on a different CPU to where the guest it relates to is running, which would cause the guest timer interrupt not to be delivered until after the next guest exit. Signed-off-by: James Hogan Cc: Paolo Bonzini Cc: Gleb Natapov Cc: kvm@vger.kernel.org Cc: Ralf Baechle Cc: linux-mips@linux-mips.org Cc: Sanjay Lal Signed-off-by: Paolo Bonzini --- arch/mips/kvm/kvm_tlb.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'arch/mips') diff --git a/arch/mips/kvm/kvm_tlb.c b/arch/mips/kvm/kvm_tlb.c index 9d371ee0a755..d65999a9f8af 100644 --- a/arch/mips/kvm/kvm_tlb.c +++ b/arch/mips/kvm/kvm_tlb.c @@ -656,6 +656,23 @@ void kvm_local_flush_tlb_all(void) local_irq_restore(flags); } +/** + * kvm_mips_migrate_count() - Migrate timer. + * @vcpu: Virtual CPU. + * + * Migrate CP0_Count hrtimer to the current CPU by cancelling and restarting it + * if it was running prior to being cancelled. + * + * Must be called when the VCPU is migrated to a different CPU to ensure that + * timer expiry during guest execution interrupts the guest and causes the + * interrupt to be delivered in a timely manner. + */ +static void kvm_mips_migrate_count(struct kvm_vcpu *vcpu) +{ + if (hrtimer_cancel(&vcpu->arch.comparecount_timer)) + hrtimer_restart(&vcpu->arch.comparecount_timer); +} + /* Restore ASID once we are scheduled back after preemption */ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu) { @@ -691,6 +708,12 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu) if (vcpu->arch.last_sched_cpu != cpu) { kvm_info("[%d->%d]KVM VCPU[%d] switch\n", vcpu->arch.last_sched_cpu, cpu, vcpu->vcpu_id); + /* + * Migrate the timer interrupt to the current CPU so that it + * always interrupts the guest and synchronously triggers a + * guest timer interrupt. + */ + kvm_mips_migrate_count(vcpu); } if (!newasid) { -- cgit v1.2.3 From e30492bbe95a2495930aa7db7eacde5141e45332 Mon Sep 17 00:00:00 2001 From: James Hogan Date: Thu, 29 May 2014 10:16:35 +0100 Subject: MIPS: KVM: Rewrite count/compare timer emulation Previously the emulation of the CPU timer was just enough to get a Linux guest running but some shortcuts were taken: - The guest timer interrupt was hard coded to always happen every 10 ms rather than being timed to when CP0_Count would match CP0_Compare. - The guest's CP0_Count register was based on the host's CP0_Count register. This isn't very portable and fails on cores without a CP_Count register implemented such as Ingenic XBurst. It also meant that the guest's CP0_Cause.DC bit to disable the CP0_Count register took no effect. - The guest's CP0_Count register was emulated by just dividing the host's CP0_Count register by 4. This resulted in continuity problems when used as a clock source, since when the host CP0_Count overflows from 0x7fffffff to 0x80000000, the guest CP0_Count transitions discontinuously from 0x1fffffff to 0xe0000000. Therefore rewrite & fix emulation of the guest timer based on the monotonic kernel time (i.e. ktime_get()). Internally a 32-bit count_bias value is added to the frequency scaled nanosecond monotonic time to get the guest's CP0_Count. The frequency of the timer is initialised to 100MHz and cannot yet be changed, but a later patch will allow the frequency to be configured via the KVM_{GET,SET}_ONE_REG ioctl interface. The timer can now be stopped via the CP0_Cause.DC bit (by the guest or via the KVM_SET_ONE_REG ioctl interface), at which point the current CP0_Count is stored and can be read directly. When it is restarted the bias is recalculated such that the CP0_Count value is continuous. Due to the nature of hrtimer interrupts any read of the guest's CP0_Count register while it is running triggers a check for whether the hrtimer has expired, so that the guest/userland cannot observe the CP0_Count passing CP0_Compare without queuing a timer interrupt. This is also taken advantage of when stopping the timer to ensure that a pending timer interrupt is queued. This replaces the implementation of: - Guest read of CP0_Count - Guest write of CP0_Count - Guest write of CP0_Compare - Guest write of CP0_Cause - Guest read of HWR 2 (CC) with RDHWR - Host read of CP0_Count via KVM_GET_ONE_REG ioctl interface - Host write of CP0_Count via KVM_SET_ONE_REG ioctl interface - Host write of CP0_Compare via KVM_SET_ONE_REG ioctl interface - Host write of CP0_Cause via KVM_SET_ONE_REG ioctl interface Signed-off-by: James Hogan Cc: Paolo Bonzini Cc: Gleb Natapov Cc: kvm@vger.kernel.org Cc: Ralf Baechle Cc: linux-mips@linux-mips.org Cc: Sanjay Lal Signed-off-by: Paolo Bonzini --- arch/mips/include/asm/kvm_host.h | 21 ++- arch/mips/kvm/kvm_mips.c | 10 +- arch/mips/kvm/kvm_mips_emul.c | 393 ++++++++++++++++++++++++++++++++++++--- arch/mips/kvm/kvm_trap_emul.c | 27 ++- 4 files changed, 413 insertions(+), 38 deletions(-) (limited to 'arch/mips') diff --git a/arch/mips/include/asm/kvm_host.h b/arch/mips/include/asm/kvm_host.h index 79410f85a5a7..75ed94aeefe7 100644 --- a/arch/mips/include/asm/kvm_host.h +++ b/arch/mips/include/asm/kvm_host.h @@ -404,8 +404,15 @@ struct kvm_vcpu_arch { u32 io_gpr; /* GPR used as IO source/target */ - /* Used to calibrate the virutal count register for the guest */ - int32_t host_cp0_count; + struct hrtimer comparecount_timer; + /* Count bias from the raw time */ + uint32_t count_bias; + /* Frequency of timer in Hz */ + uint32_t count_hz; + /* Dynamic nanosecond bias (multiple of count_period) to avoid overflow */ + s64 count_dyn_bias; + /* Period of timer tick in ns */ + u64 count_period; /* Bitmask of exceptions that are pending */ unsigned long pending_exceptions; @@ -426,8 +433,6 @@ struct kvm_vcpu_arch { uint32_t guest_kernel_asid[NR_CPUS]; struct mm_struct guest_kernel_mm, guest_user_mm; - struct hrtimer comparecount_timer; - int last_sched_cpu; /* WAIT executed */ @@ -705,7 +710,13 @@ extern enum emulation_result kvm_mips_emulate_bp_exc(unsigned long cause, extern enum emulation_result kvm_mips_complete_mmio_load(struct kvm_vcpu *vcpu, struct kvm_run *run); -enum emulation_result kvm_mips_emulate_count(struct kvm_vcpu *vcpu); +uint32_t kvm_mips_read_count(struct kvm_vcpu *vcpu); +void kvm_mips_write_count(struct kvm_vcpu *vcpu, uint32_t count); +void kvm_mips_write_compare(struct kvm_vcpu *vcpu, uint32_t compare); +void kvm_mips_init_count(struct kvm_vcpu *vcpu); +void kvm_mips_count_enable_cause(struct kvm_vcpu *vcpu); +void kvm_mips_count_disable_cause(struct kvm_vcpu *vcpu); +enum hrtimer_restart kvm_mips_count_timeout(struct kvm_vcpu *vcpu); enum emulation_result kvm_mips_check_privilege(unsigned long cause, uint32_t *opc, diff --git a/arch/mips/kvm/kvm_mips.c b/arch/mips/kvm/kvm_mips.c index 0a9c7ab56df1..fc5e44d827fc 100644 --- a/arch/mips/kvm/kvm_mips.c +++ b/arch/mips/kvm/kvm_mips.c @@ -363,7 +363,7 @@ struct kvm_vcpu *kvm_arch_vcpu_create(struct kvm *kvm, unsigned int id) vcpu->arch.last_sched_cpu = -1; /* Start off the timer */ - kvm_mips_emulate_count(vcpu); + kvm_mips_init_count(vcpu); return vcpu; @@ -707,9 +707,6 @@ static int kvm_mips_set_reg(struct kvm_vcpu *vcpu, case KVM_REG_MIPS_CP0_STATUS: kvm_write_c0_guest_status(cop0, v); break; - case KVM_REG_MIPS_CP0_CAUSE: - kvm_write_c0_guest_cause(cop0, v); - break; case KVM_REG_MIPS_CP0_EPC: kvm_write_c0_guest_epc(cop0, v); break; @@ -719,6 +716,7 @@ static int kvm_mips_set_reg(struct kvm_vcpu *vcpu, /* registers to be handled specially */ case KVM_REG_MIPS_CP0_COUNT: case KVM_REG_MIPS_CP0_COMPARE: + case KVM_REG_MIPS_CP0_CAUSE: return kvm_mips_callbacks->set_one_reg(vcpu, reg, v); default: return -EINVAL; @@ -992,9 +990,7 @@ enum hrtimer_restart kvm_mips_comparecount_wakeup(struct hrtimer *timer) vcpu = container_of(timer, struct kvm_vcpu, arch.comparecount_timer); kvm_mips_comparecount_func((unsigned long) vcpu); - hrtimer_forward_now(&vcpu->arch.comparecount_timer, - ktime_set(0, MS_TO_NS(10))); - return HRTIMER_RESTART; + return kvm_mips_count_timeout(vcpu); } int kvm_arch_vcpu_init(struct kvm_vcpu *vcpu) diff --git a/arch/mips/kvm/kvm_mips_emul.c b/arch/mips/kvm/kvm_mips_emul.c index bad31c6235d4..088c25d73a11 100644 --- a/arch/mips/kvm/kvm_mips_emul.c +++ b/arch/mips/kvm/kvm_mips_emul.c @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -228,25 +229,364 @@ enum emulation_result update_pc(struct kvm_vcpu *vcpu, uint32_t cause) return er; } -/* Everytime the compare register is written to, we need to decide when to fire - * the timer that represents timer ticks to the GUEST. +/** + * kvm_mips_count_disabled() - Find whether the CP0_Count timer is disabled. + * @vcpu: Virtual CPU. * + * Returns: 1 if the CP0_Count timer is disabled by the guest CP0_Cause.DC + * bit. + * 0 otherwise (in which case CP0_Count timer is running). */ -enum emulation_result kvm_mips_emulate_count(struct kvm_vcpu *vcpu) +static inline int kvm_mips_count_disabled(struct kvm_vcpu *vcpu) { struct mips_coproc *cop0 = vcpu->arch.cop0; - enum emulation_result er = EMULATE_DONE; + return kvm_read_c0_guest_cause(cop0) & CAUSEF_DC; +} - /* If COUNT is enabled */ - if (!(kvm_read_c0_guest_cause(cop0) & CAUSEF_DC)) { - hrtimer_try_to_cancel(&vcpu->arch.comparecount_timer); - hrtimer_start(&vcpu->arch.comparecount_timer, - ktime_set(0, MS_TO_NS(10)), HRTIMER_MODE_REL); - } else { - hrtimer_try_to_cancel(&vcpu->arch.comparecount_timer); +/** + * kvm_mips_ktime_to_count() - Scale ktime_t to a 32-bit count. + * + * Caches the dynamic nanosecond bias in vcpu->arch.count_dyn_bias. + * + * Assumes !kvm_mips_count_disabled(@vcpu) (guest CP0_Count timer is running). + */ +static uint32_t kvm_mips_ktime_to_count(struct kvm_vcpu *vcpu, ktime_t now) +{ + s64 now_ns, periods; + u64 delta; + + now_ns = ktime_to_ns(now); + delta = now_ns + vcpu->arch.count_dyn_bias; + + if (delta >= vcpu->arch.count_period) { + /* If delta is out of safe range the bias needs adjusting */ + periods = div64_s64(now_ns, vcpu->arch.count_period); + vcpu->arch.count_dyn_bias = -periods * vcpu->arch.count_period; + /* Recalculate delta with new bias */ + delta = now_ns + vcpu->arch.count_dyn_bias; } - return er; + /* + * We've ensured that: + * delta < count_period + * + * Therefore the intermediate delta*count_hz will never overflow since + * at the boundary condition: + * delta = count_period + * delta = NSEC_PER_SEC * 2^32 / count_hz + * delta * count_hz = NSEC_PER_SEC * 2^32 + */ + return div_u64(delta * vcpu->arch.count_hz, NSEC_PER_SEC); +} + +/** + * kvm_mips_read_count_running() - Read the current count value as if running. + * @vcpu: Virtual CPU. + * @now: Kernel time to read CP0_Count at. + * + * Returns the current guest CP0_Count register at time @now and handles if the + * timer interrupt is pending and hasn't been handled yet. + * + * Returns: The current value of the guest CP0_Count register. + */ +static uint32_t kvm_mips_read_count_running(struct kvm_vcpu *vcpu, ktime_t now) +{ + ktime_t expires; + int running; + + /* Is the hrtimer pending? */ + expires = hrtimer_get_expires(&vcpu->arch.comparecount_timer); + if (ktime_compare(now, expires) >= 0) { + /* + * Cancel it while we handle it so there's no chance of + * interference with the timeout handler. + */ + running = hrtimer_cancel(&vcpu->arch.comparecount_timer); + + /* Nothing should be waiting on the timeout */ + kvm_mips_callbacks->queue_timer_int(vcpu); + + /* + * Restart the timer if it was running based on the expiry time + * we read, so that we don't push it back 2 periods. + */ + if (running) { + expires = ktime_add_ns(expires, + vcpu->arch.count_period); + hrtimer_start(&vcpu->arch.comparecount_timer, expires, + HRTIMER_MODE_ABS); + } + } + + /* Return the biased and scaled guest CP0_Count */ + return vcpu->arch.count_bias + kvm_mips_ktime_to_count(vcpu, now); +} + +/** + * kvm_mips_read_count() - Read the current count value. + * @vcpu: Virtual CPU. + * + * Read the current guest CP0_Count value, taking into account whether the timer + * is stopped. + * + * Returns: The current guest CP0_Count value. + */ +uint32_t kvm_mips_read_count(struct kvm_vcpu *vcpu) +{ + struct mips_coproc *cop0 = vcpu->arch.cop0; + + /* If count disabled just read static copy of count */ + if (kvm_mips_count_disabled(vcpu)) + return kvm_read_c0_guest_count(cop0); + + return kvm_mips_read_count_running(vcpu, ktime_get()); +} + +/** + * kvm_mips_freeze_hrtimer() - Safely stop the hrtimer. + * @vcpu: Virtual CPU. + * @count: Output pointer for CP0_Count value at point of freeze. + * + * Freeze the hrtimer safely and return both the ktime and the CP0_Count value + * at the point it was frozen. It is guaranteed that any pending interrupts at + * the point it was frozen are handled, and none after that point. + * + * This is useful where the time/CP0_Count is needed in the calculation of the + * new parameters. + * + * Assumes !kvm_mips_count_disabled(@vcpu) (guest CP0_Count timer is running). + * + * Returns: The ktime at the point of freeze. + */ +static ktime_t kvm_mips_freeze_hrtimer(struct kvm_vcpu *vcpu, + uint32_t *count) +{ + ktime_t now; + + /* stop hrtimer before finding time */ + hrtimer_cancel(&vcpu->arch.comparecount_timer); + now = ktime_get(); + + /* find count at this point and handle pending hrtimer */ + *count = kvm_mips_read_count_running(vcpu, now); + + return now; +} + + +/** + * kvm_mips_resume_hrtimer() - Resume hrtimer, updating expiry. + * @vcpu: Virtual CPU. + * @now: ktime at point of resume. + * @count: CP0_Count at point of resume. + * + * Resumes the timer and updates the timer expiry based on @now and @count. + * This can be used in conjunction with kvm_mips_freeze_timer() when timer + * parameters need to be changed. + * + * It is guaranteed that a timer interrupt immediately after resume will be + * handled, but not if CP_Compare is exactly at @count. That case is already + * handled by kvm_mips_freeze_timer(). + * + * Assumes !kvm_mips_count_disabled(@vcpu) (guest CP0_Count timer is running). + */ +static void kvm_mips_resume_hrtimer(struct kvm_vcpu *vcpu, + ktime_t now, uint32_t count) +{ + struct mips_coproc *cop0 = vcpu->arch.cop0; + uint32_t compare; + u64 delta; + ktime_t expire; + + /* Calculate timeout (wrap 0 to 2^32) */ + compare = kvm_read_c0_guest_compare(cop0); + delta = (u64)(uint32_t)(compare - count - 1) + 1; + delta = div_u64(delta * NSEC_PER_SEC, vcpu->arch.count_hz); + expire = ktime_add_ns(now, delta); + + /* Update hrtimer to use new timeout */ + hrtimer_cancel(&vcpu->arch.comparecount_timer); + hrtimer_start(&vcpu->arch.comparecount_timer, expire, HRTIMER_MODE_ABS); +} + +/** + * kvm_mips_update_hrtimer() - Update next expiry time of hrtimer. + * @vcpu: Virtual CPU. + * + * Recalculates and updates the expiry time of the hrtimer. This can be used + * after timer parameters have been altered which do not depend on the time that + * the change occurs (in those cases kvm_mips_freeze_hrtimer() and + * kvm_mips_resume_hrtimer() are used directly). + * + * It is guaranteed that no timer interrupts will be lost in the process. + * + * Assumes !kvm_mips_count_disabled(@vcpu) (guest CP0_Count timer is running). + */ +static void kvm_mips_update_hrtimer(struct kvm_vcpu *vcpu) +{ + ktime_t now; + uint32_t count; + + /* + * freeze_hrtimer takes care of a timer interrupts <= count, and + * resume_hrtimer the hrtimer takes care of a timer interrupts > count. + */ + now = kvm_mips_freeze_hrtimer(vcpu, &count); + kvm_mips_resume_hrtimer(vcpu, now, count); +} + +/** + * kvm_mips_write_count() - Modify the count and update timer. + * @vcpu: Virtual CPU. + * @count: Guest CP0_Count value to set. + * + * Sets the CP0_Count value and updates the timer accordingly. + */ +void kvm_mips_write_count(struct kvm_vcpu *vcpu, uint32_t count) +{ + struct mips_coproc *cop0 = vcpu->arch.cop0; + ktime_t now; + + /* Calculate bias */ + now = ktime_get(); + vcpu->arch.count_bias = count - kvm_mips_ktime_to_count(vcpu, now); + + if (kvm_mips_count_disabled(vcpu)) + /* The timer's disabled, adjust the static count */ + kvm_write_c0_guest_count(cop0, count); + else + /* Update timeout */ + kvm_mips_resume_hrtimer(vcpu, now, count); +} + +/** + * kvm_mips_init_count() - Initialise timer. + * @vcpu: Virtual CPU. + * + * Initialise the timer to a sensible frequency, namely 100MHz, zero it, and set + * it going if it's enabled. + */ +void kvm_mips_init_count(struct kvm_vcpu *vcpu) +{ + /* 100 MHz */ + vcpu->arch.count_hz = 100*1000*1000; + vcpu->arch.count_period = div_u64((u64)NSEC_PER_SEC << 32, + vcpu->arch.count_hz); + vcpu->arch.count_dyn_bias = 0; + + /* Starting at 0 */ + kvm_mips_write_count(vcpu, 0); +} + +/** + * kvm_mips_write_compare() - Modify compare and update timer. + * @vcpu: Virtual CPU. + * @compare: New CP0_Compare value. + * + * Update CP0_Compare to a new value and update the timeout. + */ +void kvm_mips_write_compare(struct kvm_vcpu *vcpu, uint32_t compare) +{ + struct mips_coproc *cop0 = vcpu->arch.cop0; + + /* if unchanged, must just be an ack */ + if (kvm_read_c0_guest_compare(cop0) == compare) + return; + + /* Update compare */ + kvm_write_c0_guest_compare(cop0, compare); + + /* Update timeout if count enabled */ + if (!kvm_mips_count_disabled(vcpu)) + kvm_mips_update_hrtimer(vcpu); +} + +/** + * kvm_mips_count_disable() - Disable count. + * @vcpu: Virtual CPU. + * + * Disable the CP0_Count timer. A timer interrupt on or before the final stop + * time will be handled but not after. + * + * Assumes CP0_Count was previously enabled but now Guest.CP0_Cause.DC has been + * set (count disabled). + * + * Returns: The time that the timer was stopped. + */ +static ktime_t kvm_mips_count_disable(struct kvm_vcpu *vcpu) +{ + struct mips_coproc *cop0 = vcpu->arch.cop0; + uint32_t count; + ktime_t now; + + /* Stop hrtimer */ + hrtimer_cancel(&vcpu->arch.comparecount_timer); + + /* Set the static count from the dynamic count, handling pending TI */ + now = ktime_get(); + count = kvm_mips_read_count_running(vcpu, now); + kvm_write_c0_guest_count(cop0, count); + + return now; +} + +/** + * kvm_mips_count_disable_cause() - Disable count using CP0_Cause.DC. + * @vcpu: Virtual CPU. + * + * Disable the CP0_Count timer and set CP0_Cause.DC. A timer interrupt on or + * before the final stop time will be handled, but not after. + * + * Assumes CP0_Cause.DC is clear (count enabled). + */ +void kvm_mips_count_disable_cause(struct kvm_vcpu *vcpu) +{ + struct mips_coproc *cop0 = vcpu->arch.cop0; + + kvm_set_c0_guest_cause(cop0, CAUSEF_DC); + kvm_mips_count_disable(vcpu); +} + +/** + * kvm_mips_count_enable_cause() - Enable count using CP0_Cause.DC. + * @vcpu: Virtual CPU. + * + * Enable the CP0_Count timer and clear CP0_Cause.DC. A timer interrupt after + * the start time will be handled, potentially before even returning, so the + * caller should be careful with ordering of CP0_Cause modifications so as not + * to lose it. + * + * Assumes CP0_Cause.DC is set (count disabled). + */ +void kvm_mips_count_enable_cause(struct kvm_vcpu *vcpu) +{ + struct mips_coproc *cop0 = vcpu->arch.cop0; + uint32_t count; + + kvm_clear_c0_guest_cause(cop0, CAUSEF_DC); + + /* + * Set the dynamic count to match the static count. + * This starts the hrtimer. + */ + count = kvm_read_c0_guest_count(cop0); + kvm_mips_write_count(vcpu, count); +} + +/** + * kvm_mips_count_timeout() - Push timer forward on timeout. + * @vcpu: Virtual CPU. + * + * Handle an hrtimer event by push the hrtimer forward a period. + * + * Returns: The hrtimer_restart value to return to the hrtimer subsystem. + */ +enum hrtimer_restart kvm_mips_count_timeout(struct kvm_vcpu *vcpu) +{ + /* Add the Count period to the current expiry time */ + hrtimer_add_expires_ns(&vcpu->arch.comparecount_timer, + vcpu->arch.count_period); + return HRTIMER_RESTART; } enum emulation_result kvm_mips_emul_eret(struct kvm_vcpu *vcpu) @@ -471,8 +811,7 @@ kvm_mips_emulate_CP0(uint32_t inst, uint32_t *opc, uint32_t cause, #endif /* Get reg */ if ((rd == MIPS_CP0_COUNT) && (sel == 0)) { - /* XXXKYMA: Run the Guest count register @ 1/4 the rate of the host */ - vcpu->arch.gprs[rt] = (read_c0_count() >> 2); + vcpu->arch.gprs[rt] = kvm_mips_read_count(vcpu); } else if ((rd == MIPS_CP0_ERRCTL) && (sel == 0)) { vcpu->arch.gprs[rt] = 0x0; #ifdef CONFIG_KVM_MIPS_DYN_TRANS @@ -539,10 +878,7 @@ kvm_mips_emulate_CP0(uint32_t inst, uint32_t *opc, uint32_t cause, } /* Are we writing to COUNT */ else if ((rd == MIPS_CP0_COUNT) && (sel == 0)) { - /* Linux doesn't seem to write into COUNT, we throw an error - * if we notice a write to COUNT - */ - /*er = EMULATE_FAIL; */ + kvm_mips_write_count(vcpu, vcpu->arch.gprs[rt]); goto done; } else if ((rd == MIPS_CP0_COMPARE) && (sel == 0)) { kvm_debug("[%#x] MTCz, COMPARE %#lx <- %#lx\n", @@ -552,8 +888,8 @@ kvm_mips_emulate_CP0(uint32_t inst, uint32_t *opc, uint32_t cause, /* If we are writing to COMPARE */ /* Clear pending timer interrupt, if any */ kvm_mips_callbacks->dequeue_timer_int(vcpu); - kvm_write_c0_guest_compare(cop0, - vcpu->arch.gprs[rt]); + kvm_mips_write_compare(vcpu, + vcpu->arch.gprs[rt]); } else if ((rd == MIPS_CP0_STATUS) && (sel == 0)) { kvm_write_c0_guest_status(cop0, vcpu->arch.gprs[rt]); @@ -564,6 +900,20 @@ kvm_mips_emulate_CP0(uint32_t inst, uint32_t *opc, uint32_t cause, #ifdef CONFIG_KVM_MIPS_DYN_TRANS kvm_mips_trans_mtc0(inst, opc, vcpu); #endif + } else if ((rd == MIPS_CP0_CAUSE) && (sel == 0)) { + uint32_t old_cause, new_cause; + old_cause = kvm_read_c0_guest_cause(cop0); + new_cause = vcpu->arch.gprs[rt]; + /* Update R/W bits */ + kvm_change_c0_guest_cause(cop0, 0x08800300, + new_cause); + /* DC bit enabling/disabling timer? */ + if ((old_cause ^ new_cause) & CAUSEF_DC) { + if (new_cause & CAUSEF_DC) + kvm_mips_count_disable_cause(vcpu); + else + kvm_mips_count_enable_cause(vcpu); + } } else { cop0->reg[rd][sel] = vcpu->arch.gprs[rt]; #ifdef CONFIG_KVM_MIPS_DYN_TRANS @@ -1553,8 +1903,7 @@ kvm_mips_handle_ri(unsigned long cause, uint32_t *opc, current_cpu_data.icache.linesz); break; case 2: /* Read count register */ - printk("RDHWR: Cont register\n"); - arch->gprs[rt] = kvm_read_c0_guest_count(cop0); + arch->gprs[rt] = kvm_mips_read_count(vcpu); break; case 3: /* Count register resolution */ switch (current_cpu_data.cputype) { diff --git a/arch/mips/kvm/kvm_trap_emul.c b/arch/mips/kvm/kvm_trap_emul.c index f1e8389f8d33..9908f2b0ff46 100644 --- a/arch/mips/kvm/kvm_trap_emul.c +++ b/arch/mips/kvm/kvm_trap_emul.c @@ -407,8 +407,7 @@ static int kvm_trap_emul_get_one_reg(struct kvm_vcpu *vcpu, { switch (reg->id) { case KVM_REG_MIPS_CP0_COUNT: - /* XXXKYMA: Run the Guest count register @ 1/4 the rate of the host */ - *v = (read_c0_count() >> 2); + *v = kvm_mips_read_count(vcpu); break; default: return -EINVAL; @@ -424,10 +423,30 @@ static int kvm_trap_emul_set_one_reg(struct kvm_vcpu *vcpu, switch (reg->id) { case KVM_REG_MIPS_CP0_COUNT: - /* Not supported yet */ + kvm_mips_write_count(vcpu, v); break; case KVM_REG_MIPS_CP0_COMPARE: - kvm_write_c0_guest_compare(cop0, v); + kvm_mips_write_compare(vcpu, v); + break; + case KVM_REG_MIPS_CP0_CAUSE: + /* + * If the timer is stopped or started (DC bit) it must look + * atomic with changes to the interrupt pending bits (TI, IRQ5). + * A timer interrupt should not happen in between. + */ + if ((kvm_read_c0_guest_cause(cop0) ^ v) & CAUSEF_DC) { + if (v & CAUSEF_DC) { + /* disable timer first */ + kvm_mips_count_disable_cause(vcpu); + kvm_change_c0_guest_cause(cop0, ~CAUSEF_DC, v); + } else { + /* enable timer last */ + kvm_change_c0_guest_cause(cop0, ~CAUSEF_DC, v); + kvm_mips_count_enable_cause(vcpu); + } + } else { + kvm_write_c0_guest_cause(cop0, v); + } break; default: return -EINVAL; -- cgit v1.2.3 From eda3d33c685ff62524500681249e4c8e2e8dbb8c Mon Sep 17 00:00:00 2001 From: James Hogan Date: Thu, 29 May 2014 10:16:36 +0100 Subject: MIPS: KVM: Override guest kernel timer frequency directly The KVM_HOST_FREQ Kconfig symbol was used by KVM guest kernels to override the timer frequency calculation to a value based on the host frequency. Now that the KVM timer emulation is implemented independent of the host timer frequency and defaults to 100MHz, adjust the working of CONFIG_KVM_HOST_FREQ to match. The Kconfig symbol now specifies the guest timer frequency directly, and has been renamed accordingly to KVM_GUEST_TIMER_FREQ. It now defaults to 100MHz too and the help text is updated to make it clear that a zero value will allow the normal timer frequency calculation to take place (based on the emulated RTC). Signed-off-by: James Hogan Cc: Paolo Bonzini Cc: Gleb Natapov Cc: kvm@vger.kernel.org Cc: linux-mips@linux-mips.org Cc: Sanjay Lal Acked-by: Ralf Baechle Signed-off-by: Paolo Bonzini --- arch/mips/Kconfig | 12 ++++++------ arch/mips/mti-malta/malta-time.c | 14 ++------------ 2 files changed, 8 insertions(+), 18 deletions(-) (limited to 'arch/mips') diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index 5cd695f905a1..5e0014e864f3 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -1756,14 +1756,14 @@ config KVM_GUEST help Select this option if building a guest kernel for KVM (Trap & Emulate) mode -config KVM_HOST_FREQ - int "KVM Host Processor Frequency (MHz)" +config KVM_GUEST_TIMER_FREQ + int "Count/Compare Timer Frequency (MHz)" depends on KVM_GUEST - default 500 + default 100 help - Select this option if building a guest kernel for KVM to skip - RTC emulation when determining guest CPU Frequency. Instead, the guest - processor frequency is automatically derived from the host frequency. + Set this to non-zero if building a guest kernel for KVM to skip RTC + emulation when determining guest CPU Frequency. Instead, the guest's + timer frequency is specified directly. choice prompt "Kernel page size" diff --git a/arch/mips/mti-malta/malta-time.c b/arch/mips/mti-malta/malta-time.c index 319009912142..3778a359f3ad 100644 --- a/arch/mips/mti-malta/malta-time.c +++ b/arch/mips/mti-malta/malta-time.c @@ -74,18 +74,8 @@ static void __init estimate_frequencies(void) unsigned int giccount = 0, gicstart = 0; #endif -#if defined (CONFIG_KVM_GUEST) && defined (CONFIG_KVM_HOST_FREQ) - unsigned int prid = read_c0_prid() & (PRID_COMP_MASK | PRID_IMP_MASK); - - /* - * XXXKYMA: hardwire the CPU frequency to Host Freq/4 - */ - count = (CONFIG_KVM_HOST_FREQ * 1000000) >> 3; - if ((prid != (PRID_COMP_MIPS | PRID_IMP_20KC)) && - (prid != (PRID_COMP_MIPS | PRID_IMP_25KF))) - count *= 2; - - mips_hpt_frequency = count; +#if defined(CONFIG_KVM_GUEST) && CONFIG_KVM_GUEST_TIMER_FREQ + mips_hpt_frequency = CONFIG_KVM_GUEST_TIMER_FREQ * 1000000; return; #endif -- cgit v1.2.3 From f82393426afb7c82f7618b3b4e440d8dd2b40c08 Mon Sep 17 00:00:00 2001 From: James Hogan Date: Thu, 29 May 2014 10:16:37 +0100 Subject: MIPS: KVM: Add master disable count interface Expose two new virtual registers to userland via the KVM_{GET,SET}_ONE_REG ioctls. KVM_REG_MIPS_COUNT_CTL is for timer configuration fields and just contains a master disable count bit. This can be used by userland to freeze the timer in order to read a consistent state from the timer count value and timer interrupt pending bit. This cannot be done with the CP0_Cause.DC bit because the timer interrupt pending bit (TI) is also in CP0_Cause so it would be impossible to stop the timer without also risking a race with an hrtimer interrupt and having to explicitly check whether an interrupt should have occurred. When the timer is re-enabled it resumes without losing time, i.e. the CP0_Count value jumps to what it would have been had the timer not been disabled, which would also be impossible to do from userland with CP0_Cause.DC. The timer interrupt also cannot be lost, i.e. if a timer interrupt would have occurred had the timer not been disabled it is queued when the timer is re-enabled. This works by storing the nanosecond monotonic time when the master disable is set, and using it for various operations instead of the current monotonic time (e.g. when recalculating the bias when the CP0_Count is set), until the master disable is cleared again, i.e. the timer state is read/written as it would have been at that time. This state is exposed to userland via the read-only KVM_REG_MIPS_COUNT_RESUME virtual register so that userland can determine the exact time the master disable took effect. This should allow userland to atomically save the state of the timer, and later restore it. Signed-off-by: James Hogan Cc: Paolo Bonzini Cc: Gleb Natapov Cc: kvm@vger.kernel.org Cc: Ralf Baechle Cc: linux-mips@linux-mips.org Cc: David Daney Cc: Sanjay Lal Signed-off-by: Paolo Bonzini --- arch/mips/include/asm/kvm_host.h | 6 ++ arch/mips/include/uapi/asm/kvm.h | 28 +++++++++ arch/mips/kvm/kvm_mips.c | 9 ++- arch/mips/kvm/kvm_mips_emul.c | 132 +++++++++++++++++++++++++++++++++++---- arch/mips/kvm/kvm_trap_emul.c | 15 ++++- 5 files changed, 176 insertions(+), 14 deletions(-) (limited to 'arch/mips') diff --git a/arch/mips/include/asm/kvm_host.h b/arch/mips/include/asm/kvm_host.h index 75ed94aeefe7..1deeaecbe73e 100644 --- a/arch/mips/include/asm/kvm_host.h +++ b/arch/mips/include/asm/kvm_host.h @@ -405,12 +405,16 @@ struct kvm_vcpu_arch { u32 io_gpr; /* GPR used as IO source/target */ struct hrtimer comparecount_timer; + /* Count timer control KVM register */ + uint32_t count_ctl; /* Count bias from the raw time */ uint32_t count_bias; /* Frequency of timer in Hz */ uint32_t count_hz; /* Dynamic nanosecond bias (multiple of count_period) to avoid overflow */ s64 count_dyn_bias; + /* Resume time */ + ktime_t count_resume; /* Period of timer tick in ns */ u64 count_period; @@ -714,6 +718,8 @@ uint32_t kvm_mips_read_count(struct kvm_vcpu *vcpu); void kvm_mips_write_count(struct kvm_vcpu *vcpu, uint32_t count); void kvm_mips_write_compare(struct kvm_vcpu *vcpu, uint32_t compare); void kvm_mips_init_count(struct kvm_vcpu *vcpu); +int kvm_mips_set_count_ctl(struct kvm_vcpu *vcpu, s64 count_ctl); +int kvm_mips_set_count_resume(struct kvm_vcpu *vcpu, s64 count_resume); void kvm_mips_count_enable_cause(struct kvm_vcpu *vcpu); void kvm_mips_count_disable_cause(struct kvm_vcpu *vcpu); enum hrtimer_restart kvm_mips_count_timeout(struct kvm_vcpu *vcpu); diff --git a/arch/mips/include/uapi/asm/kvm.h b/arch/mips/include/uapi/asm/kvm.h index f09ff5ae2059..f859fbada1f7 100644 --- a/arch/mips/include/uapi/asm/kvm.h +++ b/arch/mips/include/uapi/asm/kvm.h @@ -106,6 +106,34 @@ struct kvm_fpu { #define KVM_REG_MIPS_LO (KVM_REG_MIPS | KVM_REG_SIZE_U64 | 33) #define KVM_REG_MIPS_PC (KVM_REG_MIPS | KVM_REG_SIZE_U64 | 34) +/* KVM specific control registers */ + +/* + * CP0_Count control + * DC: Set 0: Master disable CP0_Count and set COUNT_RESUME to now + * Set 1: Master re-enable CP0_Count with unchanged bias, handling timer + * interrupts since COUNT_RESUME + * This can be used to freeze the timer to get a consistent snapshot of + * the CP0_Count and timer interrupt pending state, while also resuming + * safely without losing time or guest timer interrupts. + * Other: Reserved, do not change. + */ +#define KVM_REG_MIPS_COUNT_CTL (KVM_REG_MIPS | KVM_REG_SIZE_U64 | \ + 0x20000 | 0) +#define KVM_REG_MIPS_COUNT_CTL_DC 0x00000001 + +/* + * CP0_Count resume monotonic nanoseconds + * The monotonic nanosecond time of the last set of COUNT_CTL.DC (master + * disable). Any reads and writes of Count related registers while + * COUNT_CTL.DC=1 will appear to occur at this time. When COUNT_CTL.DC is + * cleared again (master enable) any timer interrupts since this time will be + * emulated. + * Modifications to times in the future are rejected. + */ +#define KVM_REG_MIPS_COUNT_RESUME (KVM_REG_MIPS | KVM_REG_SIZE_U64 | \ + 0x20000 | 1) + /* * KVM MIPS specific structures and definitions * diff --git a/arch/mips/kvm/kvm_mips.c b/arch/mips/kvm/kvm_mips.c index fc5e44d827fc..a2d5d4243f51 100644 --- a/arch/mips/kvm/kvm_mips.c +++ b/arch/mips/kvm/kvm_mips.c @@ -542,7 +542,10 @@ static u64 kvm_mips_get_one_regs[] = { KVM_REG_MIPS_CP0_CONFIG2, KVM_REG_MIPS_CP0_CONFIG3, KVM_REG_MIPS_CP0_CONFIG7, - KVM_REG_MIPS_CP0_ERROREPC + KVM_REG_MIPS_CP0_ERROREPC, + + KVM_REG_MIPS_COUNT_CTL, + KVM_REG_MIPS_COUNT_RESUME, }; static int kvm_mips_get_reg(struct kvm_vcpu *vcpu, @@ -622,6 +625,8 @@ static int kvm_mips_get_reg(struct kvm_vcpu *vcpu, break; /* registers to be handled specially */ case KVM_REG_MIPS_CP0_COUNT: + case KVM_REG_MIPS_COUNT_CTL: + case KVM_REG_MIPS_COUNT_RESUME: ret = kvm_mips_callbacks->get_one_reg(vcpu, reg, &v); if (ret) return ret; @@ -717,6 +722,8 @@ static int kvm_mips_set_reg(struct kvm_vcpu *vcpu, case KVM_REG_MIPS_CP0_COUNT: case KVM_REG_MIPS_CP0_COMPARE: case KVM_REG_MIPS_CP0_CAUSE: + case KVM_REG_MIPS_COUNT_CTL: + case KVM_REG_MIPS_COUNT_RESUME: return kvm_mips_callbacks->set_one_reg(vcpu, reg, v); default: return -EINVAL; diff --git a/arch/mips/kvm/kvm_mips_emul.c b/arch/mips/kvm/kvm_mips_emul.c index 088c25d73a11..65c8dea6d1f5 100644 --- a/arch/mips/kvm/kvm_mips_emul.c +++ b/arch/mips/kvm/kvm_mips_emul.c @@ -233,14 +233,15 @@ enum emulation_result update_pc(struct kvm_vcpu *vcpu, uint32_t cause) * kvm_mips_count_disabled() - Find whether the CP0_Count timer is disabled. * @vcpu: Virtual CPU. * - * Returns: 1 if the CP0_Count timer is disabled by the guest CP0_Cause.DC - * bit. + * Returns: 1 if the CP0_Count timer is disabled by either the guest + * CP0_Cause.DC bit or the count_ctl.DC bit. * 0 otherwise (in which case CP0_Count timer is running). */ static inline int kvm_mips_count_disabled(struct kvm_vcpu *vcpu) { struct mips_coproc *cop0 = vcpu->arch.cop0; - return kvm_read_c0_guest_cause(cop0) & CAUSEF_DC; + return (vcpu->arch.count_ctl & KVM_REG_MIPS_COUNT_CTL_DC) || + (kvm_read_c0_guest_cause(cop0) & CAUSEF_DC); } /** @@ -279,6 +280,24 @@ static uint32_t kvm_mips_ktime_to_count(struct kvm_vcpu *vcpu, ktime_t now) return div_u64(delta * vcpu->arch.count_hz, NSEC_PER_SEC); } +/** + * kvm_mips_count_time() - Get effective current time. + * @vcpu: Virtual CPU. + * + * Get effective monotonic ktime. This is usually a straightforward ktime_get(), + * except when the master disable bit is set in count_ctl, in which case it is + * count_resume, i.e. the time that the count was disabled. + * + * Returns: Effective monotonic ktime for CP0_Count. + */ +static inline ktime_t kvm_mips_count_time(struct kvm_vcpu *vcpu) +{ + if (unlikely(vcpu->arch.count_ctl & KVM_REG_MIPS_COUNT_CTL_DC)) + return vcpu->arch.count_resume; + + return ktime_get(); +} + /** * kvm_mips_read_count_running() - Read the current count value as if running. * @vcpu: Virtual CPU. @@ -448,7 +467,7 @@ void kvm_mips_write_count(struct kvm_vcpu *vcpu, uint32_t count) ktime_t now; /* Calculate bias */ - now = ktime_get(); + now = kvm_mips_count_time(vcpu); vcpu->arch.count_bias = count - kvm_mips_ktime_to_count(vcpu, now); if (kvm_mips_count_disabled(vcpu)) @@ -508,8 +527,8 @@ void kvm_mips_write_compare(struct kvm_vcpu *vcpu, uint32_t compare) * Disable the CP0_Count timer. A timer interrupt on or before the final stop * time will be handled but not after. * - * Assumes CP0_Count was previously enabled but now Guest.CP0_Cause.DC has been - * set (count disabled). + * Assumes CP0_Count was previously enabled but now Guest.CP0_Cause.DC or + * count_ctl.DC has been set (count disabled). * * Returns: The time that the timer was stopped. */ @@ -535,7 +554,8 @@ static ktime_t kvm_mips_count_disable(struct kvm_vcpu *vcpu) * @vcpu: Virtual CPU. * * Disable the CP0_Count timer and set CP0_Cause.DC. A timer interrupt on or - * before the final stop time will be handled, but not after. + * before the final stop time will be handled if the timer isn't disabled by + * count_ctl.DC, but not after. * * Assumes CP0_Cause.DC is clear (count enabled). */ @@ -544,7 +564,8 @@ void kvm_mips_count_disable_cause(struct kvm_vcpu *vcpu) struct mips_coproc *cop0 = vcpu->arch.cop0; kvm_set_c0_guest_cause(cop0, CAUSEF_DC); - kvm_mips_count_disable(vcpu); + if (!(vcpu->arch.count_ctl & KVM_REG_MIPS_COUNT_CTL_DC)) + kvm_mips_count_disable(vcpu); } /** @@ -552,9 +573,9 @@ void kvm_mips_count_disable_cause(struct kvm_vcpu *vcpu) * @vcpu: Virtual CPU. * * Enable the CP0_Count timer and clear CP0_Cause.DC. A timer interrupt after - * the start time will be handled, potentially before even returning, so the - * caller should be careful with ordering of CP0_Cause modifications so as not - * to lose it. + * the start time will be handled if the timer isn't disabled by count_ctl.DC, + * potentially before even returning, so the caller should be careful with + * ordering of CP0_Cause modifications so as not to lose it. * * Assumes CP0_Cause.DC is set (count disabled). */ @@ -567,12 +588,99 @@ void kvm_mips_count_enable_cause(struct kvm_vcpu *vcpu) /* * Set the dynamic count to match the static count. - * This starts the hrtimer. + * This starts the hrtimer if count_ctl.DC allows it. + * Otherwise it conveniently updates the biases. */ count = kvm_read_c0_guest_count(cop0); kvm_mips_write_count(vcpu, count); } +/** + * kvm_mips_set_count_ctl() - Update the count control KVM register. + * @vcpu: Virtual CPU. + * @count_ctl: Count control register new value. + * + * Set the count control KVM register. The timer is updated accordingly. + * + * Returns: -EINVAL if reserved bits are set. + * 0 on success. + */ +int kvm_mips_set_count_ctl(struct kvm_vcpu *vcpu, s64 count_ctl) +{ + struct mips_coproc *cop0 = vcpu->arch.cop0; + s64 changed = count_ctl ^ vcpu->arch.count_ctl; + s64 delta; + ktime_t expire, now; + uint32_t count, compare; + + /* Only allow defined bits to be changed */ + if (changed & ~(s64)(KVM_REG_MIPS_COUNT_CTL_DC)) + return -EINVAL; + + /* Apply new value */ + vcpu->arch.count_ctl = count_ctl; + + /* Master CP0_Count disable */ + if (changed & KVM_REG_MIPS_COUNT_CTL_DC) { + /* Is CP0_Cause.DC already disabling CP0_Count? */ + if (kvm_read_c0_guest_cause(cop0) & CAUSEF_DC) { + if (count_ctl & KVM_REG_MIPS_COUNT_CTL_DC) + /* Just record the current time */ + vcpu->arch.count_resume = ktime_get(); + } else if (count_ctl & KVM_REG_MIPS_COUNT_CTL_DC) { + /* disable timer and record current time */ + vcpu->arch.count_resume = kvm_mips_count_disable(vcpu); + } else { + /* + * Calculate timeout relative to static count at resume + * time (wrap 0 to 2^32). + */ + count = kvm_read_c0_guest_count(cop0); + compare = kvm_read_c0_guest_compare(cop0); + delta = (u64)(uint32_t)(compare - count - 1) + 1; + delta = div_u64(delta * NSEC_PER_SEC, + vcpu->arch.count_hz); + expire = ktime_add_ns(vcpu->arch.count_resume, delta); + + /* Handle pending interrupt */ + now = ktime_get(); + if (ktime_compare(now, expire) >= 0) + /* Nothing should be waiting on the timeout */ + kvm_mips_callbacks->queue_timer_int(vcpu); + + /* Resume hrtimer without changing bias */ + count = kvm_mips_read_count_running(vcpu, now); + kvm_mips_resume_hrtimer(vcpu, now, count); + } + } + + return 0; +} + +/** + * kvm_mips_set_count_resume() - Update the count resume KVM register. + * @vcpu: Virtual CPU. + * @count_resume: Count resume register new value. + * + * Set the count resume KVM register. + * + * Returns: -EINVAL if out of valid range (0..now). + * 0 on success. + */ +int kvm_mips_set_count_resume(struct kvm_vcpu *vcpu, s64 count_resume) +{ + /* + * It doesn't make sense for the resume time to be in the future, as it + * would be possible for the next interrupt to be more than a full + * period in the future. + */ + if (count_resume < 0 || count_resume > ktime_to_ns(ktime_get())) + return -EINVAL; + + vcpu->arch.count_resume = ns_to_ktime(count_resume); + return 0; +} + /** * kvm_mips_count_timeout() - Push timer forward on timeout. * @vcpu: Virtual CPU. diff --git a/arch/mips/kvm/kvm_trap_emul.c b/arch/mips/kvm/kvm_trap_emul.c index 9908f2b0ff46..854502bcc749 100644 --- a/arch/mips/kvm/kvm_trap_emul.c +++ b/arch/mips/kvm/kvm_trap_emul.c @@ -409,6 +409,12 @@ static int kvm_trap_emul_get_one_reg(struct kvm_vcpu *vcpu, case KVM_REG_MIPS_CP0_COUNT: *v = kvm_mips_read_count(vcpu); break; + case KVM_REG_MIPS_COUNT_CTL: + *v = vcpu->arch.count_ctl; + break; + case KVM_REG_MIPS_COUNT_RESUME: + *v = ktime_to_ns(vcpu->arch.count_resume); + break; default: return -EINVAL; } @@ -420,6 +426,7 @@ static int kvm_trap_emul_set_one_reg(struct kvm_vcpu *vcpu, s64 v) { struct mips_coproc *cop0 = vcpu->arch.cop0; + int ret = 0; switch (reg->id) { case KVM_REG_MIPS_CP0_COUNT: @@ -448,10 +455,16 @@ static int kvm_trap_emul_set_one_reg(struct kvm_vcpu *vcpu, kvm_write_c0_guest_cause(cop0, v); } break; + case KVM_REG_MIPS_COUNT_CTL: + ret = kvm_mips_set_count_ctl(vcpu, v); + break; + case KVM_REG_MIPS_COUNT_RESUME: + ret = kvm_mips_set_count_resume(vcpu, v); + break; default: return -EINVAL; } - return 0; + return ret; } static struct kvm_mips_callbacks kvm_trap_emul_callbacks = { -- cgit v1.2.3 From f74a8e224e32edccade63a23bc260b141b067b1d Mon Sep 17 00:00:00 2001 From: James Hogan Date: Thu, 29 May 2014 10:16:38 +0100 Subject: MIPS: KVM: Add count frequency KVM register Expose the KVM guest CP0_Count frequency to userland via a new KVM_REG_MIPS_COUNT_HZ register accessible with the KVM_{GET,SET}_ONE_REG ioctls. When the frequency is altered the bias is adjusted such that the guest CP0_Count doesn't jump discontinuously or lose any timer interrupts. Signed-off-by: James Hogan Cc: Paolo Bonzini Cc: Gleb Natapov Cc: kvm@vger.kernel.org Cc: Ralf Baechle Cc: linux-mips@linux-mips.org Cc: David Daney Cc: Sanjay Lal Signed-off-by: Paolo Bonzini --- arch/mips/include/asm/kvm_host.h | 1 + arch/mips/include/uapi/asm/kvm.h | 7 ++++++ arch/mips/kvm/kvm_mips.c | 3 +++ arch/mips/kvm/kvm_mips_emul.c | 48 ++++++++++++++++++++++++++++++++++++++++ arch/mips/kvm/kvm_trap_emul.c | 6 +++++ 5 files changed, 65 insertions(+) (limited to 'arch/mips') diff --git a/arch/mips/include/asm/kvm_host.h b/arch/mips/include/asm/kvm_host.h index 1deeaecbe73e..f9c672f729ea 100644 --- a/arch/mips/include/asm/kvm_host.h +++ b/arch/mips/include/asm/kvm_host.h @@ -720,6 +720,7 @@ void kvm_mips_write_compare(struct kvm_vcpu *vcpu, uint32_t compare); void kvm_mips_init_count(struct kvm_vcpu *vcpu); int kvm_mips_set_count_ctl(struct kvm_vcpu *vcpu, s64 count_ctl); int kvm_mips_set_count_resume(struct kvm_vcpu *vcpu, s64 count_resume); +int kvm_mips_set_count_hz(struct kvm_vcpu *vcpu, s64 count_hz); void kvm_mips_count_enable_cause(struct kvm_vcpu *vcpu); void kvm_mips_count_disable_cause(struct kvm_vcpu *vcpu); enum hrtimer_restart kvm_mips_count_timeout(struct kvm_vcpu *vcpu); diff --git a/arch/mips/include/uapi/asm/kvm.h b/arch/mips/include/uapi/asm/kvm.h index f859fbada1f7..2c04b6d9ff85 100644 --- a/arch/mips/include/uapi/asm/kvm.h +++ b/arch/mips/include/uapi/asm/kvm.h @@ -133,6 +133,13 @@ struct kvm_fpu { */ #define KVM_REG_MIPS_COUNT_RESUME (KVM_REG_MIPS | KVM_REG_SIZE_U64 | \ 0x20000 | 1) +/* + * CP0_Count rate in Hz + * Specifies the rate of the CP0_Count timer in Hz. Modifications occur without + * discontinuities in CP0_Count. + */ +#define KVM_REG_MIPS_COUNT_HZ (KVM_REG_MIPS | KVM_REG_SIZE_U64 | \ + 0x20000 | 2) /* * KVM MIPS specific structures and definitions diff --git a/arch/mips/kvm/kvm_mips.c b/arch/mips/kvm/kvm_mips.c index a2d5d4243f51..7ccb7a532c55 100644 --- a/arch/mips/kvm/kvm_mips.c +++ b/arch/mips/kvm/kvm_mips.c @@ -546,6 +546,7 @@ static u64 kvm_mips_get_one_regs[] = { KVM_REG_MIPS_COUNT_CTL, KVM_REG_MIPS_COUNT_RESUME, + KVM_REG_MIPS_COUNT_HZ, }; static int kvm_mips_get_reg(struct kvm_vcpu *vcpu, @@ -627,6 +628,7 @@ static int kvm_mips_get_reg(struct kvm_vcpu *vcpu, case KVM_REG_MIPS_CP0_COUNT: case KVM_REG_MIPS_COUNT_CTL: case KVM_REG_MIPS_COUNT_RESUME: + case KVM_REG_MIPS_COUNT_HZ: ret = kvm_mips_callbacks->get_one_reg(vcpu, reg, &v); if (ret) return ret; @@ -724,6 +726,7 @@ static int kvm_mips_set_reg(struct kvm_vcpu *vcpu, case KVM_REG_MIPS_CP0_CAUSE: case KVM_REG_MIPS_COUNT_CTL: case KVM_REG_MIPS_COUNT_RESUME: + case KVM_REG_MIPS_COUNT_HZ: return kvm_mips_callbacks->set_one_reg(vcpu, reg, v); default: return -EINVAL; diff --git a/arch/mips/kvm/kvm_mips_emul.c b/arch/mips/kvm/kvm_mips_emul.c index 65c8dea6d1f5..6d257384c9b4 100644 --- a/arch/mips/kvm/kvm_mips_emul.c +++ b/arch/mips/kvm/kvm_mips_emul.c @@ -497,6 +497,54 @@ void kvm_mips_init_count(struct kvm_vcpu *vcpu) kvm_mips_write_count(vcpu, 0); } +/** + * kvm_mips_set_count_hz() - Update the frequency of the timer. + * @vcpu: Virtual CPU. + * @count_hz: Frequency of CP0_Count timer in Hz. + * + * Change the frequency of the CP0_Count timer. This is done atomically so that + * CP0_Count is continuous and no timer interrupt is lost. + * + * Returns: -EINVAL if @count_hz is out of range. + * 0 on success. + */ +int kvm_mips_set_count_hz(struct kvm_vcpu *vcpu, s64 count_hz) +{ + struct mips_coproc *cop0 = vcpu->arch.cop0; + int dc; + ktime_t now; + u32 count; + + /* ensure the frequency is in a sensible range... */ + if (count_hz <= 0 || count_hz > NSEC_PER_SEC) + return -EINVAL; + /* ... and has actually changed */ + if (vcpu->arch.count_hz == count_hz) + return 0; + + /* Safely freeze timer so we can keep it continuous */ + dc = kvm_mips_count_disabled(vcpu); + if (dc) { + now = kvm_mips_count_time(vcpu); + count = kvm_read_c0_guest_count(cop0); + } else { + now = kvm_mips_freeze_hrtimer(vcpu, &count); + } + + /* Update the frequency */ + vcpu->arch.count_hz = count_hz; + vcpu->arch.count_period = div_u64((u64)NSEC_PER_SEC << 32, count_hz); + vcpu->arch.count_dyn_bias = 0; + + /* Calculate adjusted bias so dynamic count is unchanged */ + vcpu->arch.count_bias = count - kvm_mips_ktime_to_count(vcpu, now); + + /* Update and resume hrtimer */ + if (!dc) + kvm_mips_resume_hrtimer(vcpu, now, count); + return 0; +} + /** * kvm_mips_write_compare() - Modify compare and update timer. * @vcpu: Virtual CPU. diff --git a/arch/mips/kvm/kvm_trap_emul.c b/arch/mips/kvm/kvm_trap_emul.c index 854502bcc749..b171db324cf0 100644 --- a/arch/mips/kvm/kvm_trap_emul.c +++ b/arch/mips/kvm/kvm_trap_emul.c @@ -415,6 +415,9 @@ static int kvm_trap_emul_get_one_reg(struct kvm_vcpu *vcpu, case KVM_REG_MIPS_COUNT_RESUME: *v = ktime_to_ns(vcpu->arch.count_resume); break; + case KVM_REG_MIPS_COUNT_HZ: + *v = vcpu->arch.count_hz; + break; default: return -EINVAL; } @@ -461,6 +464,9 @@ static int kvm_trap_emul_set_one_reg(struct kvm_vcpu *vcpu, case KVM_REG_MIPS_COUNT_RESUME: ret = kvm_mips_set_count_resume(vcpu, v); break; + case KVM_REG_MIPS_COUNT_HZ: + ret = kvm_mips_set_count_hz(vcpu, v); + break; default: return -EINVAL; } -- cgit v1.2.3 From 0fae34f464d1d573d13a6c1a872d90adae435e2a Mon Sep 17 00:00:00 2001 From: James Hogan Date: Thu, 29 May 2014 10:16:39 +0100 Subject: MIPS: KVM: Make kvm_mips_comparecount_{func,wakeup} static The kvm_mips_comparecount_func() and kvm_mips_comparecount_wakeup() functions are only used within arch/mips/kvm/kvm_mips.c, so make them static. Signed-off-by: James Hogan Cc: Paolo Bonzini Cc: Gleb Natapov Cc: kvm@vger.kernel.org Cc: Ralf Baechle Cc: linux-mips@linux-mips.org Cc: Sanjay Lal Signed-off-by: Paolo Bonzini --- arch/mips/kvm/kvm_mips.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'arch/mips') diff --git a/arch/mips/kvm/kvm_mips.c b/arch/mips/kvm/kvm_mips.c index 7ccb7a532c55..97e9fe1626ce 100644 --- a/arch/mips/kvm/kvm_mips.c +++ b/arch/mips/kvm/kvm_mips.c @@ -979,7 +979,7 @@ int kvm_arch_vcpu_ioctl_get_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs) return 0; } -void kvm_mips_comparecount_func(unsigned long data) +static void kvm_mips_comparecount_func(unsigned long data) { struct kvm_vcpu *vcpu = (struct kvm_vcpu *)data; @@ -994,7 +994,7 @@ void kvm_mips_comparecount_func(unsigned long data) /* * low level hrtimer wake routine. */ -enum hrtimer_restart kvm_mips_comparecount_wakeup(struct hrtimer *timer) +static enum hrtimer_restart kvm_mips_comparecount_wakeup(struct hrtimer *timer) { struct kvm_vcpu *vcpu; -- cgit v1.2.3 From 2dca3725cbe0bc5aeb195e7c098bcebae5d40f83 Mon Sep 17 00:00:00 2001 From: James Hogan Date: Thu, 29 May 2014 10:16:40 +0100 Subject: MIPS: KVM: Whitespace fixes in kvm_mips_callbacks Fix whitespace in struct kvm_mips_callbacks function pointers. Signed-off-by: James Hogan Cc: Paolo Bonzini Cc: Gleb Natapov Cc: kvm@vger.kernel.org Cc: Ralf Baechle Cc: linux-mips@linux-mips.org Cc: Sanjay Lal Signed-off-by: Paolo Bonzini --- arch/mips/include/asm/kvm_host.h | 46 ++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 23 deletions(-) (limited to 'arch/mips') diff --git a/arch/mips/include/asm/kvm_host.h b/arch/mips/include/asm/kvm_host.h index f9c672f729ea..b0aa95565752 100644 --- a/arch/mips/include/asm/kvm_host.h +++ b/arch/mips/include/asm/kvm_host.h @@ -569,29 +569,29 @@ static inline void _kvm_atomic_change_c0_guest_reg(unsigned long *reg, struct kvm_mips_callbacks { - int (*handle_cop_unusable) (struct kvm_vcpu *vcpu); - int (*handle_tlb_mod) (struct kvm_vcpu *vcpu); - int (*handle_tlb_ld_miss) (struct kvm_vcpu *vcpu); - int (*handle_tlb_st_miss) (struct kvm_vcpu *vcpu); - int (*handle_addr_err_st) (struct kvm_vcpu *vcpu); - int (*handle_addr_err_ld) (struct kvm_vcpu *vcpu); - int (*handle_syscall) (struct kvm_vcpu *vcpu); - int (*handle_res_inst) (struct kvm_vcpu *vcpu); - int (*handle_break) (struct kvm_vcpu *vcpu); - int (*vm_init) (struct kvm *kvm); - int (*vcpu_init) (struct kvm_vcpu *vcpu); - int (*vcpu_setup) (struct kvm_vcpu *vcpu); - gpa_t(*gva_to_gpa) (gva_t gva); - void (*queue_timer_int) (struct kvm_vcpu *vcpu); - void (*dequeue_timer_int) (struct kvm_vcpu *vcpu); - void (*queue_io_int) (struct kvm_vcpu *vcpu, - struct kvm_mips_interrupt *irq); - void (*dequeue_io_int) (struct kvm_vcpu *vcpu, - struct kvm_mips_interrupt *irq); - int (*irq_deliver) (struct kvm_vcpu *vcpu, unsigned int priority, - uint32_t cause); - int (*irq_clear) (struct kvm_vcpu *vcpu, unsigned int priority, - uint32_t cause); + int (*handle_cop_unusable)(struct kvm_vcpu *vcpu); + int (*handle_tlb_mod)(struct kvm_vcpu *vcpu); + int (*handle_tlb_ld_miss)(struct kvm_vcpu *vcpu); + int (*handle_tlb_st_miss)(struct kvm_vcpu *vcpu); + int (*handle_addr_err_st)(struct kvm_vcpu *vcpu); + int (*handle_addr_err_ld)(struct kvm_vcpu *vcpu); + int (*handle_syscall)(struct kvm_vcpu *vcpu); + int (*handle_res_inst)(struct kvm_vcpu *vcpu); + int (*handle_break)(struct kvm_vcpu *vcpu); + int (*vm_init)(struct kvm *kvm); + int (*vcpu_init)(struct kvm_vcpu *vcpu); + int (*vcpu_setup)(struct kvm_vcpu *vcpu); + gpa_t (*gva_to_gpa)(gva_t gva); + void (*queue_timer_int)(struct kvm_vcpu *vcpu); + void (*dequeue_timer_int)(struct kvm_vcpu *vcpu); + void (*queue_io_int)(struct kvm_vcpu *vcpu, + struct kvm_mips_interrupt *irq); + void (*dequeue_io_int)(struct kvm_vcpu *vcpu, + struct kvm_mips_interrupt *irq); + int (*irq_deliver)(struct kvm_vcpu *vcpu, unsigned int priority, + uint32_t cause); + int (*irq_clear)(struct kvm_vcpu *vcpu, unsigned int priority, + uint32_t cause); int (*get_one_reg)(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg, s64 *v); int (*set_one_reg)(struct kvm_vcpu *vcpu, -- cgit v1.2.3 From 3d654833713ddb0464cbaee14fe75ef1e4e2ded0 Mon Sep 17 00:00:00 2001 From: James Hogan Date: Thu, 29 May 2014 10:16:41 +0100 Subject: MIPS: KVM: Fix kvm_debug bit-rottage Fix build errors when DEBUG is defined in arch/mips/kvm/. - The DEBUG code in kvm_mips_handle_tlbmod() was missing some variables. - The DEBUG code in kvm_mips_host_tlb_write() was conditional on an undefined "debug" variable. - The DEBUG code in kvm_mips_host_tlb_inv() accessed asid_map directly rather than using kvm_mips_get_user_asid(). Also fixed brace placement. Signed-off-by: James Hogan Cc: Paolo Bonzini Cc: Gleb Natapov Cc: kvm@vger.kernel.org Cc: Ralf Baechle Cc: linux-mips@linux-mips.org Cc: Sanjay Lal Signed-off-by: Paolo Bonzini --- arch/mips/kvm/kvm_mips_emul.c | 6 +++++- arch/mips/kvm/kvm_tlb.c | 14 +++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) (limited to 'arch/mips') diff --git a/arch/mips/kvm/kvm_mips_emul.c b/arch/mips/kvm/kvm_mips_emul.c index 6d257384c9b4..c81ab791b8f2 100644 --- a/arch/mips/kvm/kvm_mips_emul.c +++ b/arch/mips/kvm/kvm_mips_emul.c @@ -1831,8 +1831,12 @@ kvm_mips_handle_tlbmod(unsigned long cause, uint32_t *opc, struct kvm_run *run, struct kvm_vcpu *vcpu) { enum emulation_result er = EMULATE_DONE; - #ifdef DEBUG + struct mips_coproc *cop0 = vcpu->arch.cop0; + unsigned long entryhi = (vcpu->arch.host_cp0_badvaddr & VPN2_MASK) | + (kvm_read_c0_guest_entryhi(cop0) & ASID_MASK); + int index; + /* * If address not in the guest TLB, then we are in trouble */ diff --git a/arch/mips/kvm/kvm_tlb.c b/arch/mips/kvm/kvm_tlb.c index d65999a9f8af..994fc2384180 100644 --- a/arch/mips/kvm/kvm_tlb.c +++ b/arch/mips/kvm/kvm_tlb.c @@ -233,12 +233,9 @@ kvm_mips_host_tlb_write(struct kvm_vcpu *vcpu, unsigned long entryhi, tlbw_use_hazard(); #ifdef DEBUG - if (debug) { - kvm_debug("@ %#lx idx: %2d [entryhi(R): %#lx] " - "entrylo0(R): 0x%08lx, entrylo1(R): 0x%08lx\n", - vcpu->arch.pc, idx, read_c0_entryhi(), - read_c0_entrylo0(), read_c0_entrylo1()); - } + kvm_debug("@ %#lx idx: %2d [entryhi(R): %#lx] entrylo0(R): 0x%08lx, entrylo1(R): 0x%08lx\n", + vcpu->arch.pc, idx, read_c0_entryhi(), + read_c0_entrylo0(), read_c0_entrylo1()); #endif /* Flush D-cache */ @@ -507,10 +504,9 @@ int kvm_mips_host_tlb_inv(struct kvm_vcpu *vcpu, unsigned long va) local_irq_restore(flags); #ifdef DEBUG - if (idx > 0) { + if (idx > 0) kvm_debug("%s: Invalidated entryhi %#lx @ idx %d\n", __func__, - (va & VPN2_MASK) | (vcpu->arch.asid_map[va & ASID_MASK] & ASID_MASK), idx); - } + (va & VPN2_MASK) | kvm_mips_get_user_asid(vcpu), idx); #endif return 0; -- cgit v1.2.3 From d5c704d525aa5e2e7b2bdfbe3e99e4621e29d6bf Mon Sep 17 00:00:00 2001 From: James Hogan Date: Thu, 29 May 2014 10:16:42 +0100 Subject: MIPS: KVM: Remove ifdef DEBUG around kvm_debug kvm_debug() uses pr_debug() which is already compiled out in the absence of a DEBUG define, so remove the unnecessary ifdef DEBUG lines around kvm_debug() calls which are littered around arch/mips/kvm/. As well as generally cleaning up, this prevents future bit-rot due to DEBUG not being commonly used. Signed-off-by: James Hogan Cc: Paolo Bonzini Cc: Gleb Natapov Cc: kvm@vger.kernel.org Cc: Ralf Baechle Cc: linux-mips@linux-mips.org Cc: Sanjay Lal Signed-off-by: Paolo Bonzini --- arch/mips/kvm/kvm_mips_emul.c | 2 -- arch/mips/kvm/kvm_tlb.c | 14 -------------- arch/mips/kvm/kvm_trap_emul.c | 12 ------------ 3 files changed, 28 deletions(-) (limited to 'arch/mips') diff --git a/arch/mips/kvm/kvm_mips_emul.c b/arch/mips/kvm/kvm_mips_emul.c index c81ab791b8f2..8d4840090082 100644 --- a/arch/mips/kvm/kvm_mips_emul.c +++ b/arch/mips/kvm/kvm_mips_emul.c @@ -2319,11 +2319,9 @@ kvm_mips_handle_tlbmiss(unsigned long cause, uint32_t *opc, er = EMULATE_FAIL; } } else { -#ifdef DEBUG kvm_debug ("Injecting hi: %#lx, lo0: %#lx, lo1: %#lx into shadow host TLB\n", tlb->tlb_hi, tlb->tlb_lo0, tlb->tlb_lo1); -#endif /* OK we have a Guest TLB entry, now inject it into the shadow host TLB */ kvm_mips_handle_mapped_seg_tlb_fault(vcpu, tlb, NULL, NULL); diff --git a/arch/mips/kvm/kvm_tlb.c b/arch/mips/kvm/kvm_tlb.c index 994fc2384180..15ad06d717fd 100644 --- a/arch/mips/kvm/kvm_tlb.c +++ b/arch/mips/kvm/kvm_tlb.c @@ -232,11 +232,9 @@ kvm_mips_host_tlb_write(struct kvm_vcpu *vcpu, unsigned long entryhi, tlb_write_indexed(); tlbw_use_hazard(); -#ifdef DEBUG kvm_debug("@ %#lx idx: %2d [entryhi(R): %#lx] entrylo0(R): 0x%08lx, entrylo1(R): 0x%08lx\n", vcpu->arch.pc, idx, read_c0_entryhi(), read_c0_entrylo0(), read_c0_entrylo1()); -#endif /* Flush D-cache */ if (flush_dcache_mask) { @@ -343,11 +341,9 @@ int kvm_mips_handle_commpage_tlb_fault(unsigned long badvaddr, mtc0_tlbw_hazard(); tlbw_use_hazard(); -#ifdef DEBUG kvm_debug ("@ %#lx idx: %2d [entryhi(R): %#lx] entrylo0 (R): 0x%08lx, entrylo1(R): 0x%08lx\n", vcpu->arch.pc, read_c0_index(), read_c0_entryhi(), read_c0_entrylo0(), read_c0_entrylo1()); -#endif /* Restore old ASID */ write_c0_entryhi(old_entryhi); @@ -395,10 +391,8 @@ kvm_mips_handle_mapped_seg_tlb_fault(struct kvm_vcpu *vcpu, entrylo1 = mips3_paddr_to_tlbpfn(pfn1 << PAGE_SHIFT) | (0x3 << 3) | (tlb->tlb_lo1 & MIPS3_PG_D) | (tlb->tlb_lo1 & MIPS3_PG_V); -#ifdef DEBUG kvm_debug("@ %#lx tlb_lo0: 0x%08lx tlb_lo1: 0x%08lx\n", vcpu->arch.pc, tlb->tlb_lo0, tlb->tlb_lo1); -#endif return kvm_mips_host_tlb_write(vcpu, entryhi, entrylo0, entrylo1, tlb->tlb_mask); @@ -419,10 +413,8 @@ int kvm_mips_guest_tlb_lookup(struct kvm_vcpu *vcpu, unsigned long entryhi) } } -#ifdef DEBUG kvm_debug("%s: entryhi: %#lx, index: %d lo0: %#lx, lo1: %#lx\n", __func__, entryhi, index, tlb[i].tlb_lo0, tlb[i].tlb_lo1); -#endif return index; } @@ -456,9 +448,7 @@ int kvm_mips_host_tlb_lookup(struct kvm_vcpu *vcpu, unsigned long vaddr) local_irq_restore(flags); -#ifdef DEBUG kvm_debug("Host TLB lookup, %#lx, idx: %2d\n", vaddr, idx); -#endif return idx; } @@ -503,11 +493,9 @@ int kvm_mips_host_tlb_inv(struct kvm_vcpu *vcpu, unsigned long va) local_irq_restore(flags); -#ifdef DEBUG if (idx > 0) kvm_debug("%s: Invalidated entryhi %#lx @ idx %d\n", __func__, (va & VPN2_MASK) | kvm_mips_get_user_asid(vcpu), idx); -#endif return 0; } @@ -675,9 +663,7 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu) unsigned long flags; int newasid = 0; -#ifdef DEBUG kvm_debug("%s: vcpu %p, cpu: %d\n", __func__, vcpu, cpu); -#endif /* Alocate new kernel and user ASIDs if needed */ diff --git a/arch/mips/kvm/kvm_trap_emul.c b/arch/mips/kvm/kvm_trap_emul.c index b171db324cf0..693f952b2fbb 100644 --- a/arch/mips/kvm/kvm_trap_emul.c +++ b/arch/mips/kvm/kvm_trap_emul.c @@ -32,9 +32,7 @@ static gpa_t kvm_trap_emul_gva_to_gpa_cb(gva_t gva) gpa = KVM_INVALID_ADDR; } -#ifdef DEBUG kvm_debug("%s: gva %#lx, gpa: %#llx\n", __func__, gva, gpa); -#endif return gpa; } @@ -85,11 +83,9 @@ static int kvm_trap_emul_handle_tlb_mod(struct kvm_vcpu *vcpu) if (KVM_GUEST_KSEGX(badvaddr) < KVM_GUEST_KSEG0 || KVM_GUEST_KSEGX(badvaddr) == KVM_GUEST_KSEG23) { -#ifdef DEBUG kvm_debug ("USER/KSEG23 ADDR TLB MOD fault: cause %#lx, PC: %p, BadVaddr: %#lx\n", cause, opc, badvaddr); -#endif er = kvm_mips_handle_tlbmod(cause, opc, run, vcpu); if (er == EMULATE_DONE) @@ -138,11 +134,9 @@ static int kvm_trap_emul_handle_tlb_st_miss(struct kvm_vcpu *vcpu) } } else if (KVM_GUEST_KSEGX(badvaddr) < KVM_GUEST_KSEG0 || KVM_GUEST_KSEGX(badvaddr) == KVM_GUEST_KSEG23) { -#ifdef DEBUG kvm_debug ("USER ADDR TLB LD fault: cause %#lx, PC: %p, BadVaddr: %#lx\n", cause, opc, badvaddr); -#endif er = kvm_mips_handle_tlbmiss(cause, opc, run, vcpu); if (er == EMULATE_DONE) ret = RESUME_GUEST; @@ -188,10 +182,8 @@ static int kvm_trap_emul_handle_tlb_ld_miss(struct kvm_vcpu *vcpu) } } else if (KVM_GUEST_KSEGX(badvaddr) < KVM_GUEST_KSEG0 || KVM_GUEST_KSEGX(badvaddr) == KVM_GUEST_KSEG23) { -#ifdef DEBUG kvm_debug("USER ADDR TLB ST fault: PC: %#lx, BadVaddr: %#lx\n", vcpu->arch.pc, badvaddr); -#endif /* User Address (UA) fault, this could happen if * (1) TLB entry not present/valid in both Guest and shadow host TLBs, in this @@ -236,9 +228,7 @@ static int kvm_trap_emul_handle_addr_err_st(struct kvm_vcpu *vcpu) if (KVM_GUEST_KERNEL_MODE(vcpu) && (KSEGX(badvaddr) == CKSEG0 || KSEGX(badvaddr) == CKSEG1)) { -#ifdef DEBUG kvm_debug("Emulate Store to MMIO space\n"); -#endif er = kvm_mips_emulate_inst(cause, opc, run, vcpu); if (er == EMULATE_FAIL) { printk("Emulate Store to MMIO space failed\n"); @@ -268,9 +258,7 @@ static int kvm_trap_emul_handle_addr_err_ld(struct kvm_vcpu *vcpu) int ret = RESUME_GUEST; if (KSEGX(badvaddr) == CKSEG0 || KSEGX(badvaddr) == CKSEG1) { -#ifdef DEBUG kvm_debug("Emulate Load from MMIO space @ %#lx\n", badvaddr); -#endif er = kvm_mips_emulate_inst(cause, opc, run, vcpu); if (er == EMULATE_FAIL) { printk("Emulate Load from MMIO space failed\n"); -- cgit v1.2.3 From 6e95bfd2675ea5332b10e9414de8886243ca53ae Mon Sep 17 00:00:00 2001 From: James Hogan Date: Thu, 29 May 2014 10:16:43 +0100 Subject: MIPS: KVM: Quieten kvm_info() logging The logging from MIPS KVM is fairly noisy with kvm_info() in places where it shouldn't be, such as on VM creation and migration to a different CPU. Replace these kvm_info() calls with kvm_debug(). Signed-off-by: James Hogan Cc: Paolo Bonzini Cc: Gleb Natapov Cc: kvm@vger.kernel.org Cc: Ralf Baechle Cc: linux-mips@linux-mips.org Cc: Sanjay Lal Signed-off-by: Paolo Bonzini --- arch/mips/kvm/kvm_mips.c | 27 +++++++++++++-------------- arch/mips/kvm/kvm_tlb.c | 16 ++++++++-------- 2 files changed, 21 insertions(+), 22 deletions(-) (limited to 'arch/mips') diff --git a/arch/mips/kvm/kvm_mips.c b/arch/mips/kvm/kvm_mips.c index 97e9fe1626ce..dd7b115c26e8 100644 --- a/arch/mips/kvm/kvm_mips.c +++ b/arch/mips/kvm/kvm_mips.c @@ -125,8 +125,8 @@ static void kvm_mips_init_vm_percpu(void *arg) int kvm_arch_init_vm(struct kvm *kvm, unsigned long type) { if (atomic_inc_return(&kvm_mips_instance) == 1) { - kvm_info("%s: 1st KVM instance, setup host TLB parameters\n", - __func__); + kvm_debug("%s: 1st KVM instance, setup host TLB parameters\n", + __func__); on_each_cpu(kvm_mips_init_vm_percpu, kvm, 1); } @@ -181,8 +181,8 @@ void kvm_arch_destroy_vm(struct kvm *kvm) /* If this is the last instance, restore wired count */ if (atomic_dec_return(&kvm_mips_instance) == 0) { - kvm_info("%s: last KVM instance, restoring TLB parameters\n", - __func__); + kvm_debug("%s: last KVM instance, restoring TLB parameters\n", + __func__); on_each_cpu(kvm_mips_uninit_tlbs, NULL, 1); } } @@ -244,9 +244,8 @@ void kvm_arch_commit_memory_region(struct kvm *kvm, goto out; } - kvm_info - ("Allocated space for Guest PMAP Table (%ld pages) @ %p\n", - npages, kvm->arch.guest_pmap); + kvm_debug("Allocated space for Guest PMAP Table (%ld pages) @ %p\n", + npages, kvm->arch.guest_pmap); /* Now setup the page table */ for (i = 0; i < npages; i++) { @@ -291,7 +290,7 @@ struct kvm_vcpu *kvm_arch_vcpu_create(struct kvm *kvm, unsigned int id) if (err) goto out_free_cpu; - kvm_info("kvm @ %p: create cpu %d at %p\n", kvm, id, vcpu); + kvm_debug("kvm @ %p: create cpu %d at %p\n", kvm, id, vcpu); /* Allocate space for host mode exception handlers that handle * guest mode exits @@ -311,8 +310,8 @@ struct kvm_vcpu *kvm_arch_vcpu_create(struct kvm *kvm, unsigned int id) err = -ENOMEM; goto out_free_cpu; } - kvm_info("Allocated %d bytes for KVM Exception Handlers @ %p\n", - ALIGN(size, PAGE_SIZE), gebase); + kvm_debug("Allocated %d bytes for KVM Exception Handlers @ %p\n", + ALIGN(size, PAGE_SIZE), gebase); /* Save new ebase */ vcpu->arch.guest_ebase = gebase; @@ -337,9 +336,9 @@ struct kvm_vcpu *kvm_arch_vcpu_create(struct kvm *kvm, unsigned int id) /* General handler, relocate to unmapped space for sanity's sake */ offset = 0x2000; - kvm_info("Installing KVM Exception handlers @ %p, %#x bytes\n", - gebase + offset, - mips32_GuestExceptionEnd - mips32_GuestException); + kvm_debug("Installing KVM Exception handlers @ %p, %#x bytes\n", + gebase + offset, + mips32_GuestExceptionEnd - mips32_GuestException); memcpy(gebase + offset, mips32_GuestException, mips32_GuestExceptionEnd - mips32_GuestException); @@ -356,7 +355,7 @@ struct kvm_vcpu *kvm_arch_vcpu_create(struct kvm *kvm, unsigned int id) goto out_free_gebase; } - kvm_info("Allocated COMM page @ %p\n", vcpu->arch.kseg0_commpage); + kvm_debug("Allocated COMM page @ %p\n", vcpu->arch.kseg0_commpage); kvm_mips_commpage_init(vcpu); /* Init */ diff --git a/arch/mips/kvm/kvm_tlb.c b/arch/mips/kvm/kvm_tlb.c index 15ad06d717fd..8a5a700ad8de 100644 --- a/arch/mips/kvm/kvm_tlb.c +++ b/arch/mips/kvm/kvm_tlb.c @@ -679,17 +679,17 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu) vcpu->arch.guest_user_mm.context.asid[cpu]; newasid++; - kvm_info("[%d]: cpu_context: %#lx\n", cpu, - cpu_context(cpu, current->mm)); - kvm_info("[%d]: Allocated new ASID for Guest Kernel: %#x\n", - cpu, vcpu->arch.guest_kernel_asid[cpu]); - kvm_info("[%d]: Allocated new ASID for Guest User: %#x\n", cpu, - vcpu->arch.guest_user_asid[cpu]); + kvm_debug("[%d]: cpu_context: %#lx\n", cpu, + cpu_context(cpu, current->mm)); + kvm_debug("[%d]: Allocated new ASID for Guest Kernel: %#x\n", + cpu, vcpu->arch.guest_kernel_asid[cpu]); + kvm_debug("[%d]: Allocated new ASID for Guest User: %#x\n", cpu, + vcpu->arch.guest_user_asid[cpu]); } if (vcpu->arch.last_sched_cpu != cpu) { - kvm_info("[%d->%d]KVM VCPU[%d] switch\n", - vcpu->arch.last_sched_cpu, cpu, vcpu->vcpu_id); + kvm_debug("[%d->%d]KVM VCPU[%d] switch\n", + vcpu->arch.last_sched_cpu, cpu, vcpu->vcpu_id); /* * Migrate the timer interrupt to the current CPU so that it * always interrupts the guest and synchronously triggers a -- cgit v1.2.3 From c6c0a6637f9da54f9472144d44f71cf847f92e20 Mon Sep 17 00:00:00 2001 From: James Hogan Date: Thu, 29 May 2014 10:16:44 +0100 Subject: MIPS: KVM: Remove redundant NULL checks before kfree() The kfree() function already NULL checks the parameter so remove the redundant NULL checks before kfree() calls in arch/mips/kvm/. Signed-off-by: James Hogan Cc: Paolo Bonzini Cc: Gleb Natapov Cc: kvm@vger.kernel.org Cc: Ralf Baechle Cc: linux-mips@linux-mips.org Cc: Sanjay Lal Signed-off-by: Paolo Bonzini --- arch/mips/kvm/kvm_mips.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) (limited to 'arch/mips') diff --git a/arch/mips/kvm/kvm_mips.c b/arch/mips/kvm/kvm_mips.c index dd7b115c26e8..eaa81aa15e02 100644 --- a/arch/mips/kvm/kvm_mips.c +++ b/arch/mips/kvm/kvm_mips.c @@ -144,9 +144,7 @@ void kvm_mips_free_vcpus(struct kvm *kvm) if (kvm->arch.guest_pmap[i] != KVM_INVALID_PAGE) kvm_mips_release_pfn_clean(kvm->arch.guest_pmap[i]); } - - if (kvm->arch.guest_pmap) - kfree(kvm->arch.guest_pmap); + kfree(kvm->arch.guest_pmap); kvm_for_each_vcpu(i, vcpu, kvm) { kvm_arch_vcpu_free(vcpu); @@ -384,12 +382,8 @@ void kvm_arch_vcpu_free(struct kvm_vcpu *vcpu) kvm_mips_dump_stats(vcpu); - if (vcpu->arch.guest_ebase) - kfree(vcpu->arch.guest_ebase); - - if (vcpu->arch.kseg0_commpage) - kfree(vcpu->arch.kseg0_commpage); - + kfree(vcpu->arch.guest_ebase); + kfree(vcpu->arch.kseg0_commpage); } void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu) -- cgit v1.2.3 From ee1a725f449d6e631405755f16f3c60c49e8fec5 Mon Sep 17 00:00:00 2001 From: James Hogan Date: Thu, 29 May 2014 10:16:45 +0100 Subject: MIPS: KVM: Remove redundant semicolon Remove extra semicolon in kvm_arch_vcpu_dump_regs(). Signed-off-by: James Hogan Cc: Paolo Bonzini Cc: Gleb Natapov Cc: kvm@vger.kernel.org Cc: Ralf Baechle Cc: linux-mips@linux-mips.org Cc: Sanjay Lal Signed-off-by: Paolo Bonzini --- arch/mips/kvm/kvm_mips.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'arch/mips') diff --git a/arch/mips/kvm/kvm_mips.c b/arch/mips/kvm/kvm_mips.c index eaa81aa15e02..cd5e4f568439 100644 --- a/arch/mips/kvm/kvm_mips.c +++ b/arch/mips/kvm/kvm_mips.c @@ -923,7 +923,7 @@ int kvm_arch_vcpu_dump_regs(struct kvm_vcpu *vcpu) return -1; printk("VCPU Register Dump:\n"); - printk("\tpc = 0x%08lx\n", vcpu->arch.pc);; + printk("\tpc = 0x%08lx\n", vcpu->arch.pc); printk("\texceptions: %08lx\n", vcpu->arch.pending_exceptions); for (i = 0; i < 32; i += 4) { -- cgit v1.2.3