diff options
Diffstat (limited to 'arch/powerpc/kvm/book3s_hv_rmhandlers.S')
| -rw-r--r-- | arch/powerpc/kvm/book3s_hv_rmhandlers.S | 237 | 
1 files changed, 170 insertions, 67 deletions
diff --git a/arch/powerpc/kvm/book3s_hv_rmhandlers.S b/arch/powerpc/kvm/book3s_hv_rmhandlers.S index 10b6c358dd7..b02f91e4c70 100644 --- a/arch/powerpc/kvm/book3s_hv_rmhandlers.S +++ b/arch/powerpc/kvm/book3s_hv_rmhandlers.S @@ -79,10 +79,6 @@ _GLOBAL(kvmppc_hv_entry_trampoline)   *                                                                            *   *****************************************************************************/ -#define XICS_XIRR		4 -#define XICS_QIRR		0xc -#define XICS_IPI		2	/* interrupt source # for IPIs */ -  /*   * We come in here when wakened from nap mode on a secondary hw thread.   * Relocation is off and most register values are lost. @@ -101,50 +97,51 @@ kvm_start_guest:  	li	r0,1  	stb	r0,PACA_NAPSTATELOST(r13) -	/* get vcpu pointer, NULL if we have no vcpu to run */ -	ld	r4,HSTATE_KVM_VCPU(r13) -	cmpdi	cr1,r4,0 +	/* were we napping due to cede? */ +	lbz	r0,HSTATE_NAPPING(r13) +	cmpwi	r0,0 +	bne	kvm_end_cede + +	/* +	 * We weren't napping due to cede, so this must be a secondary +	 * thread being woken up to run a guest, or being woken up due +	 * to a stray IPI.  (Or due to some machine check or hypervisor +	 * maintenance interrupt while the core is in KVM.) +	 */  	/* Check the wake reason in SRR1 to see why we got here */  	mfspr	r3,SPRN_SRR1  	rlwinm	r3,r3,44-31,0x7		/* extract wake reason field */  	cmpwi	r3,4			/* was it an external interrupt? */ -	bne	27f - -	/* -	 * External interrupt - for now assume it is an IPI, since we -	 * should never get any other interrupts sent to offline threads. -	 * Only do this for secondary threads. -	 */ -	beq	cr1,25f -	lwz	r3,VCPU_PTID(r4) -	cmpwi	r3,0 -	beq	27f -25:	ld	r5,HSTATE_XICS_PHYS(r13) -	li	r0,0xff -	li	r6,XICS_QIRR -	li	r7,XICS_XIRR +	bne	27f			/* if not */ +	ld	r5,HSTATE_XICS_PHYS(r13) +	li	r7,XICS_XIRR		/* if it was an external interrupt, */  	lwzcix	r8,r5,r7		/* get and ack the interrupt */  	sync  	clrldi.	r9,r8,40		/* get interrupt source ID. */ -	beq	27f			/* none there? */ -	cmpwi	r9,XICS_IPI -	bne	26f +	beq	28f			/* none there? */ +	cmpwi	r9,XICS_IPI		/* was it an IPI? */ +	bne	29f +	li	r0,0xff +	li	r6,XICS_MFRR  	stbcix	r0,r5,r6		/* clear IPI */ -26:	stwcix	r8,r5,r7		/* EOI the interrupt */ +	stwcix	r8,r5,r7		/* EOI the interrupt */ +	sync				/* order loading of vcpu after that */ -27:	/* XXX should handle hypervisor maintenance interrupts etc. here */ - -	/* reload vcpu pointer after clearing the IPI */ +	/* get vcpu pointer, NULL if we have no vcpu to run */  	ld	r4,HSTATE_KVM_VCPU(r13)  	cmpdi	r4,0  	/* if we have no vcpu to run, go back to sleep */  	beq	kvm_no_guest +	b	kvmppc_hv_entry -	/* were we napping due to cede? */ -	lbz	r0,HSTATE_NAPPING(r13) -	cmpwi	r0,0 -	bne	kvm_end_cede +27:	/* XXX should handle hypervisor maintenance interrupts etc. here */ +	b	kvm_no_guest +28:	/* SRR1 said external but ICP said nope?? */ +	b	kvm_no_guest +29:	/* External non-IPI interrupt to offline secondary thread? help?? */ +	stw	r8,HSTATE_SAVED_XIRR(r13) +	b	kvm_no_guest  .global kvmppc_hv_entry  kvmppc_hv_entry: @@ -260,6 +257,8 @@ END_FTR_SECTION_IFSET(CPU_FTR_ARCH_206)  	lwz	r5, LPPACA_YIELDCOUNT(r3)  	addi	r5, r5, 1  	stw	r5, LPPACA_YIELDCOUNT(r3) +	li	r6, 1 +	stb	r6, VCPU_VPA_DIRTY(r4)  25:  	/* Load up DAR and DSISR */  	ld	r5, VCPU_DAR(r4) @@ -485,20 +484,20 @@ toc_tlbie_lock:  	mtctr	r6  	mtxer	r7 +	ld	r10, VCPU_PC(r4) +	ld	r11, VCPU_MSR(r4)  kvmppc_cede_reentry:		/* r4 = vcpu, r13 = paca */  	ld	r6, VCPU_SRR0(r4)  	ld	r7, VCPU_SRR1(r4) -	ld	r10, VCPU_PC(r4) -	ld	r11, VCPU_MSR(r4)	/* r11 = vcpu->arch.msr & ~MSR_HV */ +	/* r11 = vcpu->arch.msr & ~MSR_HV */  	rldicl	r11, r11, 63 - MSR_HV_LG, 1  	rotldi	r11, r11, 1 + MSR_HV_LG  	ori	r11, r11, MSR_ME  	/* Check if we can deliver an external or decrementer interrupt now */  	ld	r0,VCPU_PENDING_EXC(r4) -	li	r8,(1 << BOOK3S_IRQPRIO_EXTERNAL) -	oris	r8,r8,(1 << BOOK3S_IRQPRIO_EXTERNAL_LEVEL)@h +	lis	r8,(1 << BOOK3S_IRQPRIO_EXTERNAL_LEVEL)@h  	and	r0,r0,r8  	cmpdi	cr1,r0,0  	andi.	r0,r11,MSR_EE @@ -526,10 +525,10 @@ END_FTR_SECTION_IFSET(CPU_FTR_ARCH_206)  	/* Move SRR0 and SRR1 into the respective regs */  5:	mtspr	SPRN_SRR0, r6  	mtspr	SPRN_SRR1, r7 -	li	r0,0 -	stb	r0,VCPU_CEDED(r4)	/* cancel cede */  fast_guest_return: +	li	r0,0 +	stb	r0,VCPU_CEDED(r4)	/* cancel cede */  	mtspr	SPRN_HSRR0,r10  	mtspr	SPRN_HSRR1,r11 @@ -539,6 +538,11 @@ fast_guest_return:  	/* Enter guest */ +BEGIN_FTR_SECTION +	ld	r5, VCPU_CFAR(r4) +	mtspr	SPRN_CFAR, r5 +END_FTR_SECTION_IFSET(CPU_FTR_CFAR) +  	ld	r5, VCPU_LR(r4)  	lwz	r6, VCPU_CR(r4)  	mtlr	r5 @@ -604,6 +608,10 @@ kvmppc_interrupt:  	lwz	r4, HSTATE_SCRATCH1(r13)  	std	r3, VCPU_GPR(R12)(r9)  	stw	r4, VCPU_CR(r9) +BEGIN_FTR_SECTION +	ld	r3, HSTATE_CFAR(r13) +	std	r3, VCPU_CFAR(r9) +END_FTR_SECTION_IFSET(CPU_FTR_CFAR)  	/* Restore R1/R2 so we can handle faults */  	ld	r1, HSTATE_HOST_R1(r13) @@ -667,17 +675,99 @@ END_FTR_SECTION_IFSET(CPU_FTR_ARCH_206)  	cmpwi	r12,BOOK3S_INTERRUPT_SYSCALL  	beq	hcall_try_real_mode -	/* Check for mediated interrupts (could be done earlier really ...) */ +	/* Only handle external interrupts here on arch 206 and later */  BEGIN_FTR_SECTION -	cmpwi	r12,BOOK3S_INTERRUPT_EXTERNAL -	bne+	1f -	andi.	r0,r11,MSR_EE -	beq	1f -	mfspr	r5,SPRN_LPCR -	andi.	r0,r5,LPCR_MER -	bne	bounce_ext_interrupt -1: -END_FTR_SECTION_IFSET(CPU_FTR_ARCH_206) +	b	ext_interrupt_to_host +END_FTR_SECTION_IFCLR(CPU_FTR_ARCH_206) + +	/* External interrupt ? */ +	cmpwi	r12, BOOK3S_INTERRUPT_EXTERNAL +	bne+	ext_interrupt_to_host + +	/* External interrupt, first check for host_ipi. If this is +	 * set, we know the host wants us out so let's do it now +	 */ +do_ext_interrupt: +	lbz	r0, HSTATE_HOST_IPI(r13) +	cmpwi	r0, 0 +	bne	ext_interrupt_to_host + +	/* Now read the interrupt from the ICP */ +	ld	r5, HSTATE_XICS_PHYS(r13) +	li	r7, XICS_XIRR +	cmpdi	r5, 0 +	beq-	ext_interrupt_to_host +	lwzcix	r3, r5, r7 +	rlwinm.	r0, r3, 0, 0xffffff +	sync +	beq	3f		/* if nothing pending in the ICP */ + +	/* We found something in the ICP... +	 * +	 * If it's not an IPI, stash it in the PACA and return to +	 * the host, we don't (yet) handle directing real external +	 * interrupts directly to the guest +	 */ +	cmpwi	r0, XICS_IPI +	bne	ext_stash_for_host + +	/* It's an IPI, clear the MFRR and EOI it */ +	li	r0, 0xff +	li	r6, XICS_MFRR +	stbcix	r0, r5, r6		/* clear the IPI */ +	stwcix	r3, r5, r7		/* EOI it */ +	sync + +	/* We need to re-check host IPI now in case it got set in the +	 * meantime. If it's clear, we bounce the interrupt to the +	 * guest +	 */ +	lbz	r0, HSTATE_HOST_IPI(r13) +	cmpwi	r0, 0 +	bne-	1f + +	/* Allright, looks like an IPI for the guest, we need to set MER */ +3: +	/* Check if any CPU is heading out to the host, if so head out too */ +	ld	r5, HSTATE_KVM_VCORE(r13) +	lwz	r0, VCORE_ENTRY_EXIT(r5) +	cmpwi	r0, 0x100 +	bge	ext_interrupt_to_host + +	/* See if there is a pending interrupt for the guest */ +	mfspr	r8, SPRN_LPCR +	ld	r0, VCPU_PENDING_EXC(r9) +	/* Insert EXTERNAL_LEVEL bit into LPCR at the MER bit position */ +	rldicl.	r0, r0, 64 - BOOK3S_IRQPRIO_EXTERNAL_LEVEL, 63 +	rldimi	r8, r0, LPCR_MER_SH, 63 - LPCR_MER_SH +	beq	2f + +	/* And if the guest EE is set, we can deliver immediately, else +	 * we return to the guest with MER set +	 */ +	andi.	r0, r11, MSR_EE +	beq	2f +	mtspr	SPRN_SRR0, r10 +	mtspr	SPRN_SRR1, r11 +	li	r10, BOOK3S_INTERRUPT_EXTERNAL +	li	r11, (MSR_ME << 1) | 1	/* synthesize MSR_SF | MSR_ME */ +	rotldi	r11, r11, 63 +2:	mr	r4, r9 +	mtspr	SPRN_LPCR, r8 +	b	fast_guest_return + +	/* We raced with the host, we need to resend that IPI, bummer */ +1:	li	r0, IPI_PRIORITY +	stbcix	r0, r5, r6		/* set the IPI */ +	sync +	b	ext_interrupt_to_host + +ext_stash_for_host: +	/* It's not an IPI and it's for the host, stash it in the PACA +	 * before exit, it will be picked up by the host ICP driver +	 */ +	stw	r3, HSTATE_SAVED_XIRR(r13) +ext_interrupt_to_host:  guest_exit_cont:		/* r9 = vcpu, r12 = trap, r13 = paca */  	/* Save DEC */ @@ -820,7 +910,7 @@ END_FTR_SECTION_IFSET(CPU_FTR_ARCH_201)  	beq	44f  	ld	r8,HSTATE_XICS_PHYS(r6)	/* get thread's XICS reg addr */  	li	r0,IPI_PRIORITY -	li	r7,XICS_QIRR +	li	r7,XICS_MFRR  	stbcix	r0,r7,r8		/* trigger the IPI */  44:	srdi.	r3,r3,1  	addi	r6,r6,PACA_SIZE @@ -1009,6 +1099,8 @@ END_FTR_SECTION_IFSET(CPU_FTR_ARCH_206)  	lwz	r3, LPPACA_YIELDCOUNT(r8)  	addi	r3, r3, 1  	stw	r3, LPPACA_YIELDCOUNT(r8) +	li	r3, 1 +	stb	r3, VCPU_VPA_DIRTY(r9)  25:  	/* Save PMU registers if requested */  	/* r8 and cr0.eq are live here */ @@ -1341,11 +1433,19 @@ hcall_real_table:  	.long	0		/* 0x58 */  	.long	0		/* 0x5c */  	.long	0		/* 0x60 */ -	.long	0		/* 0x64 */ -	.long	0		/* 0x68 */ -	.long	0		/* 0x6c */ -	.long	0		/* 0x70 */ -	.long	0		/* 0x74 */ +#ifdef CONFIG_KVM_XICS +	.long	.kvmppc_rm_h_eoi - hcall_real_table +	.long	.kvmppc_rm_h_cppr - hcall_real_table +	.long	.kvmppc_rm_h_ipi - hcall_real_table +	.long	0		/* 0x70 - H_IPOLL */ +	.long	.kvmppc_rm_h_xirr - hcall_real_table +#else +	.long	0		/* 0x64 - H_EOI */ +	.long	0		/* 0x68 - H_CPPR */ +	.long	0		/* 0x6c - H_IPI */ +	.long	0		/* 0x70 - H_IPOLL */ +	.long	0		/* 0x74 - H_XIRR */ +#endif  	.long	0		/* 0x78 */  	.long	0		/* 0x7c */  	.long	0		/* 0x80 */ @@ -1396,15 +1496,6 @@ ignore_hdec:  	mr	r4,r9  	b	fast_guest_return -bounce_ext_interrupt: -	mr	r4,r9 -	mtspr	SPRN_SRR0,r10 -	mtspr	SPRN_SRR1,r11 -	li	r10,BOOK3S_INTERRUPT_EXTERNAL -	li	r11,(MSR_ME << 1) | 1	/* synthesize MSR_SF | MSR_ME */ -	rotldi	r11,r11,63 -	b	fast_guest_return -  _GLOBAL(kvmppc_h_set_dabr)  	std	r4,VCPU_DABR(r3)  	/* Work around P7 bug where DABR can get corrupted on mtspr */ @@ -1510,6 +1601,9 @@ END_FTR_SECTION_IFCLR(CPU_FTR_ARCH_206)  	b	.  kvm_end_cede: +	/* get vcpu pointer */ +	ld	r4, HSTATE_KVM_VCPU(r13) +  	/* Woken by external or decrementer interrupt */  	ld	r1, HSTATE_HOST_R1(r13) @@ -1549,6 +1643,16 @@ kvm_end_cede:  	li	r0,0  	stb	r0,HSTATE_NAPPING(r13) +	/* Check the wake reason in SRR1 to see why we got here */ +	mfspr	r3, SPRN_SRR1 +	rlwinm	r3, r3, 44-31, 0x7	/* extract wake reason field */ +	cmpwi	r3, 4			/* was it an external interrupt? */ +	li	r12, BOOK3S_INTERRUPT_EXTERNAL +	mr	r9, r4 +	ld	r10, VCPU_PC(r9) +	ld	r11, VCPU_MSR(r9) +	beq	do_ext_interrupt	/* if so */ +  	/* see if any other thread is already exiting */  	lwz	r0,VCORE_ENTRY_EXIT(r5)  	cmpwi	r0,0x100 @@ -1568,8 +1672,7 @@ kvm_cede_prodded:  	/* we've ceded but we want to give control to the host */  kvm_cede_exit: -	li	r3,H_TOO_HARD -	blr +	b	hcall_real_fallback  	/* Try to handle a machine check in real mode */  machine_check_realmode: @@ -1617,7 +1720,7 @@ secondary_nap:  	beq	37f  	sync  	li	r0, 0xff -	li	r6, XICS_QIRR +	li	r6, XICS_MFRR  	stbcix	r0, r5, r6		/* clear the IPI */  	stwcix	r3, r5, r7		/* EOI it */  37:	sync  |