KVM: PPC: booke: category E.HV (GS-mode) support
Chips such as e500mc that implement category E.HV in Power ISA 2.06 provide hardware virtualization features, including a new MSR mode for guest state. The guest OS can perform many operations without trapping into the hypervisor, including transitions to and from guest userspace. Since we can use SRR1[GS] to reliably tell whether an exception came from guest state, instead of messing around with IVPR, we use DO_KVM similarly to book3s. Current issues include: - Machine checks from guest state are not routed to the host handler. - The guest can cause a host oops by executing an emulated instruction in a page that lacks read permission. Existing e500/4xx support has the same problem. Includes work by Ashish Kalra <Ashish.Kalra@freescale.com>, Varun Sethi <Varun.Sethi@freescale.com>, and Liu Yu <yu.liu@freescale.com>. Signed-off-by: Scott Wood <scottwood@freescale.com> [agraf: remove pt_regs usage] Signed-off-by: Alexander Graf <agraf@suse.de> Signed-off-by: Avi Kivity <avi@redhat.com>
This commit is contained in:
		
					parent
					
						
							
								cfac57847a
							
						
					
				
			
			
				commit
				
					
						d30f6e4800
					
				
			
		
					 18 changed files with 1058 additions and 67 deletions
				
			
		|  | @ -19,6 +19,7 @@ | |||
| 
 | ||||
| #define PPC_DBELL_MSG_BRDCAST	(0x04000000) | ||||
| #define PPC_DBELL_TYPE(x)	(((x) & 0xf) << (63-36)) | ||||
| #define PPC_DBELL_LPID(x)	((x) << (63 - 49)) | ||||
| enum ppc_dbell { | ||||
| 	PPC_DBELL = 0,		/* doorbell */ | ||||
| 	PPC_DBELL_CRIT = 1,	/* critical doorbell */ | ||||
|  |  | |||
|  | @ -48,6 +48,14 @@ | |||
| #define BOOKE_INTERRUPT_SPE_FP_DATA 33 | ||||
| #define BOOKE_INTERRUPT_SPE_FP_ROUND 34 | ||||
| #define BOOKE_INTERRUPT_PERFORMANCE_MONITOR 35 | ||||
| #define BOOKE_INTERRUPT_DOORBELL 36 | ||||
| #define BOOKE_INTERRUPT_DOORBELL_CRITICAL 37 | ||||
| 
 | ||||
| /* booke_hv */ | ||||
| #define BOOKE_INTERRUPT_GUEST_DBELL 38 | ||||
| #define BOOKE_INTERRUPT_GUEST_DBELL_CRIT 39 | ||||
| #define BOOKE_INTERRUPT_HV_SYSCALL 40 | ||||
| #define BOOKE_INTERRUPT_HV_PRIV 41 | ||||
| 
 | ||||
| /* book3s */ | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										49
									
								
								arch/powerpc/include/asm/kvm_booke_hv_asm.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								arch/powerpc/include/asm/kvm_booke_hv_asm.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,49 @@ | |||
| /*
 | ||||
|  * Copyright 2010-2011 Freescale Semiconductor, Inc. | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License, version 2, as | ||||
|  * published by the Free Software Foundation. | ||||
|  */ | ||||
| 
 | ||||
| #ifndef ASM_KVM_BOOKE_HV_ASM_H | ||||
| #define ASM_KVM_BOOKE_HV_ASM_H | ||||
| 
 | ||||
| #ifdef __ASSEMBLY__ | ||||
| 
 | ||||
| /*
 | ||||
|  * All exceptions from guest state must go through KVM | ||||
|  * (except for those which are delivered directly to the guest) -- | ||||
|  * there are no exceptions for which we fall through directly to | ||||
|  * the normal host handler. | ||||
|  * | ||||
|  * Expected inputs (normal exceptions): | ||||
|  *   SCRATCH0 = saved r10 | ||||
|  *   r10 = thread struct | ||||
|  *   r11 = appropriate SRR1 variant (currently used as scratch) | ||||
|  *   r13 = saved CR | ||||
|  *   *(r10 + THREAD_NORMSAVE(0)) = saved r11 | ||||
|  *   *(r10 + THREAD_NORMSAVE(2)) = saved r13 | ||||
|  * | ||||
|  * Expected inputs (crit/mcheck/debug exceptions): | ||||
|  *   appropriate SCRATCH = saved r8 | ||||
|  *   r8 = exception level stack frame | ||||
|  *   r9 = *(r8 + _CCR) = saved CR | ||||
|  *   r11 = appropriate SRR1 variant (currently used as scratch) | ||||
|  *   *(r8 + GPR9) = saved r9 | ||||
|  *   *(r8 + GPR10) = saved r10 (r10 not yet clobbered) | ||||
|  *   *(r8 + GPR11) = saved r11 | ||||
|  */ | ||||
| .macro DO_KVM intno srr1 | ||||
| #ifdef CONFIG_KVM_BOOKE_HV | ||||
| BEGIN_FTR_SECTION | ||||
| 	mtocrf	0x80, r11	/* check MSR[GS] without clobbering reg */ | ||||
| 	bf	3, kvmppc_resume_\intno\()_\srr1 | ||||
| 	b	kvmppc_handler_\intno\()_\srr1 | ||||
| kvmppc_resume_\intno\()_\srr1: | ||||
| END_FTR_SECTION_IFSET(CPU_FTR_EMB_HV) | ||||
| #endif | ||||
| .endm | ||||
| 
 | ||||
| #endif /*__ASSEMBLY__ */ | ||||
| #endif /* ASM_KVM_BOOKE_HV_ASM_H */ | ||||
|  | @ -106,6 +106,8 @@ struct kvm_vcpu_stat { | |||
| 	u32 dec_exits; | ||||
| 	u32 ext_intr_exits; | ||||
| 	u32 halt_wakeup; | ||||
| 	u32 dbell_exits; | ||||
| 	u32 gdbell_exits; | ||||
| #ifdef CONFIG_PPC_BOOK3S | ||||
| 	u32 pf_storage; | ||||
| 	u32 pf_instruc; | ||||
|  | @ -140,6 +142,7 @@ enum kvm_exit_types { | |||
| 	EMULATED_TLBSX_EXITS, | ||||
| 	EMULATED_TLBWE_EXITS, | ||||
| 	EMULATED_RFI_EXITS, | ||||
| 	EMULATED_RFCI_EXITS, | ||||
| 	DEC_EXITS, | ||||
| 	EXT_INTR_EXITS, | ||||
| 	HALT_WAKEUP, | ||||
|  | @ -147,6 +150,8 @@ enum kvm_exit_types { | |||
| 	FP_UNAVAIL, | ||||
| 	DEBUG_EXITS, | ||||
| 	TIMEINGUEST, | ||||
| 	DBELL_EXITS, | ||||
| 	GDBELL_EXITS, | ||||
| 	__NUMBER_OF_KVM_EXIT_TYPES | ||||
| }; | ||||
| 
 | ||||
|  | @ -217,10 +222,10 @@ struct kvm_arch_memory_slot { | |||
| }; | ||||
| 
 | ||||
| struct kvm_arch { | ||||
| 	unsigned int lpid; | ||||
| #ifdef CONFIG_KVM_BOOK3S_64_HV | ||||
| 	unsigned long hpt_virt; | ||||
| 	struct revmap_entry *revmap; | ||||
| 	unsigned int lpid; | ||||
| 	unsigned int host_lpid; | ||||
| 	unsigned long host_lpcr; | ||||
| 	unsigned long sdr1; | ||||
|  | @ -345,6 +350,17 @@ struct kvm_vcpu_arch { | |||
| 	u64 vsr[64]; | ||||
| #endif | ||||
| 
 | ||||
| #ifdef CONFIG_KVM_BOOKE_HV | ||||
| 	u32 host_mas4; | ||||
| 	u32 host_mas6; | ||||
| 	u32 shadow_epcr; | ||||
| 	u32 epcr; | ||||
| 	u32 shadow_msrp; | ||||
| 	u32 eplc; | ||||
| 	u32 epsc; | ||||
| 	u32 oldpir; | ||||
| #endif | ||||
| 
 | ||||
| #ifdef CONFIG_PPC_BOOK3S | ||||
| 	/* For Gekko paired singles */ | ||||
| 	u32 qpr[32]; | ||||
|  | @ -428,6 +444,7 @@ struct kvm_vcpu_arch { | |||
| 	ulong queued_esr; | ||||
| 	u32 tlbcfg[4]; | ||||
| 	u32 mmucfg; | ||||
| 	u32 epr; | ||||
| #endif | ||||
| 	gpa_t paddr_accessed; | ||||
| 
 | ||||
|  |  | |||
|  | @ -139,6 +139,9 @@ extern int kvmppc_core_prepare_memory_region(struct kvm *kvm, | |||
| extern void kvmppc_core_commit_memory_region(struct kvm *kvm, | ||||
| 				struct kvm_userspace_memory_region *mem); | ||||
| 
 | ||||
| extern int kvmppc_bookehv_init(void); | ||||
| extern void kvmppc_bookehv_exit(void); | ||||
| 
 | ||||
| /*
 | ||||
|  * Cuts out inst bits with ordering according to spec. | ||||
|  * That means the leftmost bit is zero. All given bits are included. | ||||
|  |  | |||
|  | @ -104,6 +104,8 @@ | |||
| #define MAS4_TSIZED_MASK	0x00000f80	/* Default TSIZE */ | ||||
| #define MAS4_TSIZED_SHIFT	7 | ||||
| 
 | ||||
| #define MAS5_SGS		0x80000000 | ||||
| 
 | ||||
| #define MAS6_SPID0		0x3FFF0000 | ||||
| #define MAS6_SPID1		0x00007FFE | ||||
| #define MAS6_ISIZE(x)		MAS1_TSIZE(x) | ||||
|  | @ -118,6 +120,10 @@ | |||
| 
 | ||||
| #define MAS7_RPN		0xFFFFFFFF | ||||
| 
 | ||||
| #define MAS8_TGS		0x80000000 /* Guest space */ | ||||
| #define MAS8_VF			0x40000000 /* Virtualization Fault */ | ||||
| #define MAS8_TLPID		0x000000ff | ||||
| 
 | ||||
| /* Bit definitions for MMUCFG */ | ||||
| #define MMUCFG_MAVN	0x00000003	/* MMU Architecture Version Number */ | ||||
| #define MMUCFG_MAVN_V1	0x00000000	/* v1.0 */ | ||||
|  |  | |||
|  | @ -243,6 +243,9 @@ struct thread_struct { | |||
| #ifdef CONFIG_KVM_BOOK3S_32_HANDLER | ||||
| 	void*		kvm_shadow_vcpu; /* KVM internal data */ | ||||
| #endif /* CONFIG_KVM_BOOK3S_32_HANDLER */ | ||||
| #if defined(CONFIG_KVM) && defined(CONFIG_BOOKE) | ||||
| 	struct kvm_vcpu	*kvm_vcpu; | ||||
| #endif | ||||
| #ifdef CONFIG_PPC64 | ||||
| 	unsigned long	dscr; | ||||
| 	int		dscr_inherit; | ||||
|  |  | |||
|  | @ -257,7 +257,9 @@ | |||
| #define   LPCR_LPES_SH	2 | ||||
| #define   LPCR_RMI     0x00000002      /* real mode is cache inhibit */ | ||||
| #define   LPCR_HDICE   0x00000001      /* Hyp Decr enable (HV,PR,EE) */ | ||||
| #ifndef SPRN_LPID | ||||
| #define SPRN_LPID	0x13F	/* Logical Partition Identifier */ | ||||
| #endif | ||||
| #define   LPID_RSVD	0x3ff		/* Reserved LPID for partn switching */ | ||||
| #define	SPRN_HMER	0x150	/* Hardware m? error recovery */ | ||||
| #define	SPRN_HMEER	0x151	/* Hardware m? enable error recovery */ | ||||
|  |  | |||
|  | @ -61,18 +61,30 @@ extern u32 booke_wdt_period; | |||
| #define SPRN_SPRG7W	0x117	/* Special Purpose Register General 7 Write */ | ||||
| #define SPRN_EPCR	0x133	/* Embedded Processor Control Register */ | ||||
| #define SPRN_DBCR2	0x136	/* Debug Control Register 2 */ | ||||
| #define SPRN_MSRP	0x137	/* MSR Protect Register */ | ||||
| #define SPRN_IAC3	0x13A	/* Instruction Address Compare 3 */ | ||||
| #define SPRN_IAC4	0x13B	/* Instruction Address Compare 4 */ | ||||
| #define SPRN_DVC1	0x13E	/* Data Value Compare Register 1 */ | ||||
| #define SPRN_DVC2	0x13F	/* Data Value Compare Register 2 */ | ||||
| #define SPRN_LPID	0x152	/* Logical Partition ID */ | ||||
| #define SPRN_MAS8	0x155	/* MMU Assist Register 8 */ | ||||
| #define SPRN_TLB0PS	0x158	/* TLB 0 Page Size Register */ | ||||
| #define SPRN_TLB1PS	0x159	/* TLB 1 Page Size Register */ | ||||
| #define SPRN_MAS5_MAS6	0x15c	/* MMU Assist Register 5 || 6 */ | ||||
| #define SPRN_MAS8_MAS1	0x15d	/* MMU Assist Register 8 || 1 */ | ||||
| #define SPRN_EPTCFG	0x15e	/* Embedded Page Table Config */ | ||||
| #define SPRN_GSPRG0	0x170	/* Guest SPRG0 */ | ||||
| #define SPRN_GSPRG1	0x171	/* Guest SPRG1 */ | ||||
| #define SPRN_GSPRG2	0x172	/* Guest SPRG2 */ | ||||
| #define SPRN_GSPRG3	0x173	/* Guest SPRG3 */ | ||||
| #define SPRN_MAS7_MAS3	0x174	/* MMU Assist Register 7 || 3 */ | ||||
| #define SPRN_MAS0_MAS1	0x175	/* MMU Assist Register 0 || 1 */ | ||||
| #define SPRN_GSRR0	0x17A	/* Guest SRR0 */ | ||||
| #define SPRN_GSRR1	0x17B	/* Guest SRR1 */ | ||||
| #define SPRN_GEPR	0x17C	/* Guest EPR */ | ||||
| #define SPRN_GDEAR	0x17D	/* Guest DEAR */ | ||||
| #define SPRN_GPIR	0x17E	/* Guest PIR */ | ||||
| #define SPRN_GESR	0x17F	/* Guest Exception Syndrome Register */ | ||||
| #define SPRN_IVOR0	0x190	/* Interrupt Vector Offset Register 0 */ | ||||
| #define SPRN_IVOR1	0x191	/* Interrupt Vector Offset Register 1 */ | ||||
| #define SPRN_IVOR2	0x192	/* Interrupt Vector Offset Register 2 */ | ||||
|  | @ -93,6 +105,13 @@ extern u32 booke_wdt_period; | |||
| #define SPRN_IVOR39	0x1B1	/* Interrupt Vector Offset Register 39 */ | ||||
| #define SPRN_IVOR40	0x1B2	/* Interrupt Vector Offset Register 40 */ | ||||
| #define SPRN_IVOR41	0x1B3	/* Interrupt Vector Offset Register 41 */ | ||||
| #define SPRN_GIVOR2	0x1B8	/* Guest IVOR2 */ | ||||
| #define SPRN_GIVOR3	0x1B9	/* Guest IVOR3 */ | ||||
| #define SPRN_GIVOR4	0x1BA	/* Guest IVOR4 */ | ||||
| #define SPRN_GIVOR8	0x1BB	/* Guest IVOR8 */ | ||||
| #define SPRN_GIVOR13	0x1BC	/* Guest IVOR13 */ | ||||
| #define SPRN_GIVOR14	0x1BD	/* Guest IVOR14 */ | ||||
| #define SPRN_GIVPR	0x1BF	/* Guest IVPR */ | ||||
| #define SPRN_SPEFSCR	0x200	/* SPE & Embedded FP Status & Control */ | ||||
| #define SPRN_BBEAR	0x201	/* Branch Buffer Entry Address Register */ | ||||
| #define SPRN_BBTAR	0x202	/* Branch Buffer Target Address Register */ | ||||
|  | @ -245,6 +264,10 @@ extern u32 booke_wdt_period; | |||
| #define MCSR_LDG	0x00002000UL /* Guarded Load */ | ||||
| #define MCSR_TLBSYNC	0x00000002UL /* Multiple tlbsyncs detected */ | ||||
| #define MCSR_BSL2_ERR	0x00000001UL /* Backside L2 cache error */ | ||||
| 
 | ||||
| #define MSRP_UCLEP	0x04000000 /* Protect MSR[UCLE] */ | ||||
| #define MSRP_DEP	0x00000200 /* Protect MSR[DE] */ | ||||
| #define MSRP_PMMP	0x00000004 /* Protect MSR[PMM] */ | ||||
| #endif | ||||
| 
 | ||||
| #ifdef CONFIG_E200 | ||||
|  | @ -599,6 +622,17 @@ extern u32 booke_wdt_period; | |||
| #define SPRN_EPCR_DMIUH		0x00400000	/* Disable MAS Interrupt updates | ||||
| 						 * for hypervisor */ | ||||
| 
 | ||||
| /* Bit definitions for EPLC/EPSC */ | ||||
| #define EPC_EPR		0x80000000 /* 1 = user, 0 = kernel */ | ||||
| #define EPC_EPR_SHIFT	31 | ||||
| #define EPC_EAS		0x40000000 /* Address Space */ | ||||
| #define EPC_EAS_SHIFT	30 | ||||
| #define EPC_EGS		0x20000000 /* 1 = guest, 0 = hypervisor */ | ||||
| #define EPC_EGS_SHIFT	29 | ||||
| #define EPC_ELPID	0x00ff0000 | ||||
| #define EPC_ELPID_SHIFT	16 | ||||
| #define EPC_EPID	0x00003fff | ||||
| #define EPC_EPID_SHIFT	0 | ||||
| 
 | ||||
| /*
 | ||||
|  * The IBM-403 is an even more odd special case, as it is much | ||||
|  |  | |||
|  | @ -116,6 +116,9 @@ int main(void) | |||
| #ifdef CONFIG_KVM_BOOK3S_32_HANDLER | ||||
| 	DEFINE(THREAD_KVM_SVCPU, offsetof(struct thread_struct, kvm_shadow_vcpu)); | ||||
| #endif | ||||
| #ifdef CONFIG_KVM_BOOKE_HV | ||||
| 	DEFINE(THREAD_KVM_VCPU, offsetof(struct thread_struct, kvm_vcpu)); | ||||
| #endif | ||||
| 
 | ||||
| 	DEFINE(TI_FLAGS, offsetof(struct thread_info, flags)); | ||||
| 	DEFINE(TI_LOCAL_FLAGS, offsetof(struct thread_info, local_flags)); | ||||
|  | @ -387,6 +390,7 @@ int main(void) | |||
| #ifdef CONFIG_KVM | ||||
| 	DEFINE(VCPU_HOST_STACK, offsetof(struct kvm_vcpu, arch.host_stack)); | ||||
| 	DEFINE(VCPU_HOST_PID, offsetof(struct kvm_vcpu, arch.host_pid)); | ||||
| 	DEFINE(VCPU_GUEST_PID, offsetof(struct kvm_vcpu, arch.pid)); | ||||
| 	DEFINE(VCPU_GPRS, offsetof(struct kvm_vcpu, arch.gpr)); | ||||
| 	DEFINE(VCPU_VRSAVE, offsetof(struct kvm_vcpu, arch.vrsave)); | ||||
| 	DEFINE(VCPU_FPRS, offsetof(struct kvm_vcpu, arch.fpr)); | ||||
|  | @ -429,9 +433,11 @@ int main(void) | |||
| 	DEFINE(VCPU_SHARED_MAS4, offsetof(struct kvm_vcpu_arch_shared, mas4)); | ||||
| 	DEFINE(VCPU_SHARED_MAS6, offsetof(struct kvm_vcpu_arch_shared, mas6)); | ||||
| 
 | ||||
| 	DEFINE(VCPU_KVM, offsetof(struct kvm_vcpu, kvm)); | ||||
| 	DEFINE(KVM_LPID, offsetof(struct kvm, arch.lpid)); | ||||
| 
 | ||||
| 	/* book3s */ | ||||
| #ifdef CONFIG_KVM_BOOK3S_64_HV | ||||
| 	DEFINE(KVM_LPID, offsetof(struct kvm, arch.lpid)); | ||||
| 	DEFINE(KVM_SDR1, offsetof(struct kvm, arch.sdr1)); | ||||
| 	DEFINE(KVM_HOST_LPID, offsetof(struct kvm, arch.host_lpid)); | ||||
| 	DEFINE(KVM_HOST_LPCR, offsetof(struct kvm, arch.host_lpcr)); | ||||
|  | @ -446,7 +452,6 @@ int main(void) | |||
| 	DEFINE(VCPU_DAR, offsetof(struct kvm_vcpu, arch.shregs.dar)); | ||||
| #endif | ||||
| #ifdef CONFIG_PPC_BOOK3S | ||||
| 	DEFINE(VCPU_KVM, offsetof(struct kvm_vcpu, kvm)); | ||||
| 	DEFINE(VCPU_VCPUID, offsetof(struct kvm_vcpu, vcpu_id)); | ||||
| 	DEFINE(VCPU_PURR, offsetof(struct kvm_vcpu, arch.purr)); | ||||
| 	DEFINE(VCPU_SPURR, offsetof(struct kvm_vcpu, arch.spurr)); | ||||
|  | @ -597,6 +602,12 @@ int main(void) | |||
| 	DEFINE(VCPU_HOST_SPEFSCR, offsetof(struct kvm_vcpu, arch.host_spefscr)); | ||||
| #endif | ||||
| 
 | ||||
| #ifdef CONFIG_KVM_BOOKE_HV | ||||
| 	DEFINE(VCPU_HOST_MAS4, offsetof(struct kvm_vcpu, arch.host_mas4)); | ||||
| 	DEFINE(VCPU_HOST_MAS6, offsetof(struct kvm_vcpu, arch.host_mas6)); | ||||
| 	DEFINE(VCPU_EPLC, offsetof(struct kvm_vcpu, arch.eplc)); | ||||
| #endif | ||||
| 
 | ||||
| #ifdef CONFIG_KVM_EXIT_TIMING | ||||
| 	DEFINE(VCPU_TIMING_EXIT_TBU, offsetof(struct kvm_vcpu, | ||||
| 						arch.timing_exit.tv32.tbu)); | ||||
|  |  | |||
|  | @ -3,6 +3,7 @@ | |||
| 
 | ||||
| #include <asm/ptrace.h>	/* for STACK_FRAME_REGS_MARKER */ | ||||
| #include <asm/kvm_asm.h> | ||||
| #include <asm/kvm_booke_hv_asm.h> | ||||
| 
 | ||||
| /*
 | ||||
|  * Macros used for common Book-e exception handling | ||||
|  | @ -36,8 +37,9 @@ | |||
| 	stw	r11, THREAD_NORMSAVE(0)(r10);				     \ | ||||
| 	stw	r13, THREAD_NORMSAVE(2)(r10);				     \ | ||||
| 	mfcr	r13;			/* save CR in r13 for now	   */\ | ||||
| 	mfspr	r11,SPRN_SRR1;		/* check whether user or kernel    */\ | ||||
| 	andi.	r11,r11,MSR_PR;						     \ | ||||
| 	mfspr	r11, SPRN_SRR1;		                                     \ | ||||
| 	DO_KVM	BOOKE_INTERRUPT_##intno SPRN_SRR1;			     \ | ||||
| 	andi.	r11, r11, MSR_PR;	/* check whether user or kernel    */\ | ||||
| 	mr	r11, r1;						     \ | ||||
| 	beq	1f;							     \ | ||||
| 	/* if from user, start at top of this thread's kernel stack */       \ | ||||
|  | @ -123,8 +125,9 @@ | |||
| 	stw	r10,GPR10(r8);						     \ | ||||
| 	stw	r11,GPR11(r8);						     \ | ||||
| 	stw	r9,_CCR(r8);		/* save CR on stack		   */\ | ||||
| 	mfspr	r10,exc_level_srr1;	/* check whether user or kernel    */\ | ||||
| 	andi.	r10,r10,MSR_PR;						     \ | ||||
| 	mfspr	r11,exc_level_srr1;	/* check whether user or kernel    */\ | ||||
| 	DO_KVM	BOOKE_INTERRUPT_##intno exc_level_srr1;		             \ | ||||
| 	andi.	r11,r11,MSR_PR;						     \ | ||||
| 	mfspr	r11,SPRN_SPRG_THREAD;	/* if from user, start at top of   */\ | ||||
| 	lwz	r11,THREAD_INFO-THREAD(r11); /* this thread's kernel stack */\ | ||||
| 	addi	r11,r11,EXC_LVL_FRAME_OVERHEAD;	/* allocate stack frame    */\ | ||||
|  | @ -172,6 +175,23 @@ | |||
| 		EXC_LEVEL_EXCEPTION_PROLOG(MC, MACHINE_CHECK, \ | ||||
| 			SPRN_MCSRR0, SPRN_MCSRR1) | ||||
| 
 | ||||
| /*
 | ||||
|  * Guest Doorbell -- this is a bit odd in that uses GSRR0/1 despite | ||||
|  * being delivered to the host.  This exception can only happen | ||||
|  * inside a KVM guest -- so we just handle up to the DO_KVM rather | ||||
|  * than try to fit this into one of the existing prolog macros. | ||||
|  */ | ||||
| #define GUEST_DOORBELL_EXCEPTION \ | ||||
| 	START_EXCEPTION(GuestDoorbell);					     \ | ||||
| 	mtspr	SPRN_SPRG_WSCRATCH0, r10;	/* save one register */	     \ | ||||
| 	mfspr	r10, SPRN_SPRG_THREAD;					     \ | ||||
| 	stw	r11, THREAD_NORMSAVE(0)(r10);				     \ | ||||
| 	mfspr	r11, SPRN_SRR1;		                                     \ | ||||
| 	stw	r13, THREAD_NORMSAVE(2)(r10);				     \ | ||||
| 	mfcr	r13;			/* save CR in r13 for now	   */\ | ||||
| 	DO_KVM	BOOKE_INTERRUPT_GUEST_DBELL SPRN_GSRR1;			     \ | ||||
| 	trap | ||||
| 
 | ||||
| /*
 | ||||
|  * Exception vectors. | ||||
|  */ | ||||
|  |  | |||
|  | @ -90,6 +90,9 @@ config KVM_BOOK3S_64_PR | |||
| 	depends on KVM_BOOK3S_64 && !KVM_BOOK3S_64_HV | ||||
| 	select KVM_BOOK3S_PR | ||||
| 
 | ||||
| config KVM_BOOKE_HV | ||||
| 	bool | ||||
| 
 | ||||
| config KVM_440 | ||||
| 	bool "KVM support for PowerPC 440 processors" | ||||
| 	depends on EXPERIMENTAL && 44x | ||||
|  |  | |||
|  | @ -17,6 +17,8 @@ | |||
|  * | ||||
|  * Authors: Hollis Blanchard <hollisb@us.ibm.com> | ||||
|  *          Christian Ehrhardt <ehrhardt@linux.vnet.ibm.com> | ||||
|  *          Scott Wood <scottwood@freescale.com> | ||||
|  *          Varun Sethi <varun.sethi@freescale.com> | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/errno.h> | ||||
|  | @ -30,9 +32,12 @@ | |||
| #include <asm/cputable.h> | ||||
| #include <asm/uaccess.h> | ||||
| #include <asm/kvm_ppc.h> | ||||
| #include "timing.h" | ||||
| #include <asm/cacheflush.h> | ||||
| #include <asm/dbell.h> | ||||
| #include <asm/hw_irq.h> | ||||
| #include <asm/irq.h> | ||||
| 
 | ||||
| #include "timing.h" | ||||
| #include "booke.h" | ||||
| 
 | ||||
| unsigned long kvmppc_booke_handlers; | ||||
|  | @ -55,6 +60,8 @@ struct kvm_stats_debugfs_item debugfs_entries[] = { | |||
| 	{ "dec",        VCPU_STAT(dec_exits) }, | ||||
| 	{ "ext_intr",   VCPU_STAT(ext_intr_exits) }, | ||||
| 	{ "halt_wakeup", VCPU_STAT(halt_wakeup) }, | ||||
| 	{ "doorbell", VCPU_STAT(dbell_exits) }, | ||||
| 	{ "guest doorbell", VCPU_STAT(gdbell_exits) }, | ||||
| 	{ NULL } | ||||
| }; | ||||
| 
 | ||||
|  | @ -121,6 +128,10 @@ void kvmppc_set_msr(struct kvm_vcpu *vcpu, u32 new_msr) | |||
| { | ||||
| 	u32 old_msr = vcpu->arch.shared->msr; | ||||
| 
 | ||||
| #ifdef CONFIG_KVM_BOOKE_HV | ||||
| 	new_msr |= MSR_GS; | ||||
| #endif | ||||
| 
 | ||||
| 	vcpu->arch.shared->msr = new_msr; | ||||
| 
 | ||||
| 	kvmppc_mmu_msr_notify(vcpu, old_msr); | ||||
|  | @ -195,6 +206,75 @@ void kvmppc_core_dequeue_external(struct kvm_vcpu *vcpu, | |||
| 	clear_bit(BOOKE_IRQPRIO_EXTERNAL_LEVEL, &vcpu->arch.pending_exceptions); | ||||
| } | ||||
| 
 | ||||
| static void set_guest_srr(struct kvm_vcpu *vcpu, unsigned long srr0, u32 srr1) | ||||
| { | ||||
| #ifdef CONFIG_KVM_BOOKE_HV | ||||
| 	mtspr(SPRN_GSRR0, srr0); | ||||
| 	mtspr(SPRN_GSRR1, srr1); | ||||
| #else | ||||
| 	vcpu->arch.shared->srr0 = srr0; | ||||
| 	vcpu->arch.shared->srr1 = srr1; | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| static void set_guest_csrr(struct kvm_vcpu *vcpu, unsigned long srr0, u32 srr1) | ||||
| { | ||||
| 	vcpu->arch.csrr0 = srr0; | ||||
| 	vcpu->arch.csrr1 = srr1; | ||||
| } | ||||
| 
 | ||||
| static void set_guest_dsrr(struct kvm_vcpu *vcpu, unsigned long srr0, u32 srr1) | ||||
| { | ||||
| 	if (cpu_has_feature(CPU_FTR_DEBUG_LVL_EXC)) { | ||||
| 		vcpu->arch.dsrr0 = srr0; | ||||
| 		vcpu->arch.dsrr1 = srr1; | ||||
| 	} else { | ||||
| 		set_guest_csrr(vcpu, srr0, srr1); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static void set_guest_mcsrr(struct kvm_vcpu *vcpu, unsigned long srr0, u32 srr1) | ||||
| { | ||||
| 	vcpu->arch.mcsrr0 = srr0; | ||||
| 	vcpu->arch.mcsrr1 = srr1; | ||||
| } | ||||
| 
 | ||||
| static unsigned long get_guest_dear(struct kvm_vcpu *vcpu) | ||||
| { | ||||
| #ifdef CONFIG_KVM_BOOKE_HV | ||||
| 	return mfspr(SPRN_GDEAR); | ||||
| #else | ||||
| 	return vcpu->arch.shared->dar; | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| static void set_guest_dear(struct kvm_vcpu *vcpu, unsigned long dear) | ||||
| { | ||||
| #ifdef CONFIG_KVM_BOOKE_HV | ||||
| 	mtspr(SPRN_GDEAR, dear); | ||||
| #else | ||||
| 	vcpu->arch.shared->dar = dear; | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| static unsigned long get_guest_esr(struct kvm_vcpu *vcpu) | ||||
| { | ||||
| #ifdef CONFIG_KVM_BOOKE_HV | ||||
| 	return mfspr(SPRN_GESR); | ||||
| #else | ||||
| 	return vcpu->arch.shared->esr; | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| static void set_guest_esr(struct kvm_vcpu *vcpu, u32 esr) | ||||
| { | ||||
| #ifdef CONFIG_KVM_BOOKE_HV | ||||
| 	mtspr(SPRN_GESR, esr); | ||||
| #else | ||||
| 	vcpu->arch.shared->esr = esr; | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| /* Deliver the interrupt of the corresponding priority, if possible. */ | ||||
| static int kvmppc_booke_irqprio_deliver(struct kvm_vcpu *vcpu, | ||||
|                                         unsigned int priority) | ||||
|  | @ -206,6 +286,7 @@ static int kvmppc_booke_irqprio_deliver(struct kvm_vcpu *vcpu, | |||
| 	ulong crit_r1 = kvmppc_get_gpr(vcpu, 1); | ||||
| 	bool crit; | ||||
| 	bool keep_irq = false; | ||||
| 	enum int_class int_class; | ||||
| 
 | ||||
| 	/* Truncate crit indicators in 32 bit mode */ | ||||
| 	if (!(vcpu->arch.shared->msr & MSR_SF)) { | ||||
|  | @ -241,16 +322,20 @@ static int kvmppc_booke_irqprio_deliver(struct kvm_vcpu *vcpu, | |||
| 	case BOOKE_IRQPRIO_AP_UNAVAIL: | ||||
| 	case BOOKE_IRQPRIO_ALIGNMENT: | ||||
| 		allowed = 1; | ||||
| 		msr_mask = MSR_CE|MSR_ME|MSR_DE; | ||||
| 		msr_mask = MSR_GS | MSR_CE | MSR_ME | MSR_DE; | ||||
| 		int_class = INT_CLASS_NONCRIT; | ||||
| 		break; | ||||
| 	case BOOKE_IRQPRIO_CRITICAL: | ||||
| 	case BOOKE_IRQPRIO_WATCHDOG: | ||||
| 		allowed = vcpu->arch.shared->msr & MSR_CE; | ||||
| 		msr_mask = MSR_ME; | ||||
| 		allowed = allowed && !crit; | ||||
| 		msr_mask = MSR_GS | MSR_ME; | ||||
| 		int_class = INT_CLASS_CRIT; | ||||
| 		break; | ||||
| 	case BOOKE_IRQPRIO_MACHINE_CHECK: | ||||
| 		allowed = vcpu->arch.shared->msr & MSR_ME; | ||||
| 		msr_mask = 0; | ||||
| 		allowed = allowed && !crit; | ||||
| 		msr_mask = MSR_GS; | ||||
| 		int_class = INT_CLASS_MC; | ||||
| 		break; | ||||
| 	case BOOKE_IRQPRIO_DECREMENTER: | ||||
| 	case BOOKE_IRQPRIO_FIT: | ||||
|  | @ -259,28 +344,62 @@ static int kvmppc_booke_irqprio_deliver(struct kvm_vcpu *vcpu, | |||
| 	case BOOKE_IRQPRIO_EXTERNAL: | ||||
| 		allowed = vcpu->arch.shared->msr & MSR_EE; | ||||
| 		allowed = allowed && !crit; | ||||
| 		msr_mask = MSR_CE|MSR_ME|MSR_DE; | ||||
| 		msr_mask = MSR_GS | MSR_CE | MSR_ME | MSR_DE; | ||||
| 		int_class = INT_CLASS_NONCRIT; | ||||
| 		break; | ||||
| 	case BOOKE_IRQPRIO_DEBUG: | ||||
| 		allowed = vcpu->arch.shared->msr & MSR_DE; | ||||
| 		msr_mask = MSR_ME; | ||||
| 		allowed = allowed && !crit; | ||||
| 		msr_mask = MSR_GS | MSR_ME; | ||||
| 		int_class = INT_CLASS_CRIT; | ||||
| 		break; | ||||
| 	} | ||||
| 
 | ||||
| 	if (allowed) { | ||||
| 		vcpu->arch.shared->srr0 = vcpu->arch.pc; | ||||
| 		vcpu->arch.shared->srr1 = vcpu->arch.shared->msr; | ||||
| 		switch (int_class) { | ||||
| 		case INT_CLASS_NONCRIT: | ||||
| 			set_guest_srr(vcpu, vcpu->arch.pc, | ||||
| 				      vcpu->arch.shared->msr); | ||||
| 			break; | ||||
| 		case INT_CLASS_CRIT: | ||||
| 			set_guest_csrr(vcpu, vcpu->arch.pc, | ||||
| 				       vcpu->arch.shared->msr); | ||||
| 			break; | ||||
| 		case INT_CLASS_DBG: | ||||
| 			set_guest_dsrr(vcpu, vcpu->arch.pc, | ||||
| 				       vcpu->arch.shared->msr); | ||||
| 			break; | ||||
| 		case INT_CLASS_MC: | ||||
| 			set_guest_mcsrr(vcpu, vcpu->arch.pc, | ||||
| 					vcpu->arch.shared->msr); | ||||
| 			break; | ||||
| 		} | ||||
| 
 | ||||
| 		vcpu->arch.pc = vcpu->arch.ivpr | vcpu->arch.ivor[priority]; | ||||
| 		if (update_esr == true) | ||||
| 			vcpu->arch.shared->esr = vcpu->arch.queued_esr; | ||||
| 			set_guest_esr(vcpu, vcpu->arch.queued_esr); | ||||
| 		if (update_dear == true) | ||||
| 			vcpu->arch.shared->dar = vcpu->arch.queued_dear; | ||||
| 			set_guest_dear(vcpu, vcpu->arch.queued_dear); | ||||
| 		kvmppc_set_msr(vcpu, vcpu->arch.shared->msr & msr_mask); | ||||
| 
 | ||||
| 		if (!keep_irq) | ||||
| 			clear_bit(priority, &vcpu->arch.pending_exceptions); | ||||
| 	} | ||||
| 
 | ||||
| #ifdef CONFIG_KVM_BOOKE_HV | ||||
| 	/*
 | ||||
| 	 * If an interrupt is pending but masked, raise a guest doorbell | ||||
| 	 * so that we are notified when the guest enables the relevant | ||||
| 	 * MSR bit. | ||||
| 	 */ | ||||
| 	if (vcpu->arch.pending_exceptions & BOOKE_IRQMASK_EE) | ||||
| 		kvmppc_set_pending_interrupt(vcpu, INT_CLASS_NONCRIT); | ||||
| 	if (vcpu->arch.pending_exceptions & BOOKE_IRQMASK_CE) | ||||
| 		kvmppc_set_pending_interrupt(vcpu, INT_CLASS_CRIT); | ||||
| 	if (vcpu->arch.pending_exceptions & BOOKE_IRQPRIO_MACHINE_CHECK) | ||||
| 		kvmppc_set_pending_interrupt(vcpu, INT_CLASS_MC); | ||||
| #endif | ||||
| 
 | ||||
| 	return allowed; | ||||
| } | ||||
| 
 | ||||
|  | @ -344,6 +463,11 @@ int kvmppc_vcpu_run(struct kvm_run *kvm_run, struct kvm_vcpu *vcpu) | |||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	if (!current->thread.kvm_vcpu) { | ||||
| 		WARN(1, "no vcpu\n"); | ||||
| 		return -EPERM; | ||||
| 	} | ||||
| 
 | ||||
| 	local_irq_disable(); | ||||
| 
 | ||||
| 	kvmppc_core_prepare_to_enter(vcpu); | ||||
|  | @ -363,6 +487,38 @@ out: | |||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static int emulation_exit(struct kvm_run *run, struct kvm_vcpu *vcpu) | ||||
| { | ||||
| 	enum emulation_result er; | ||||
| 
 | ||||
| 	er = kvmppc_emulate_instruction(run, vcpu); | ||||
| 	switch (er) { | ||||
| 	case EMULATE_DONE: | ||||
| 		/* don't overwrite subtypes, just account kvm_stats */ | ||||
| 		kvmppc_account_exit_stat(vcpu, EMULATED_INST_EXITS); | ||||
| 		/* Future optimization: only reload non-volatiles if
 | ||||
| 		 * they were actually modified by emulation. */ | ||||
| 		return RESUME_GUEST_NV; | ||||
| 
 | ||||
| 	case EMULATE_DO_DCR: | ||||
| 		run->exit_reason = KVM_EXIT_DCR; | ||||
| 		return RESUME_HOST; | ||||
| 
 | ||||
| 	case EMULATE_FAIL: | ||||
| 		/* XXX Deliver Program interrupt to guest. */ | ||||
| 		printk(KERN_CRIT "%s: emulation at %lx failed (%08x)\n", | ||||
| 		       __func__, vcpu->arch.pc, vcpu->arch.last_inst); | ||||
| 		/* For debugging, encode the failing instruction and
 | ||||
| 		 * report it to userspace. */ | ||||
| 		run->hw.hardware_exit_reason = ~0ULL << 32; | ||||
| 		run->hw.hardware_exit_reason |= vcpu->arch.last_inst; | ||||
| 		return RESUME_HOST; | ||||
| 
 | ||||
| 	default: | ||||
| 		BUG(); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * kvmppc_handle_exit | ||||
|  * | ||||
|  | @ -371,12 +527,30 @@ out: | |||
| int kvmppc_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu, | ||||
|                        unsigned int exit_nr) | ||||
| { | ||||
| 	enum emulation_result er; | ||||
| 	int r = RESUME_HOST; | ||||
| 
 | ||||
| 	/* update before a new last_exit_type is rewritten */ | ||||
| 	kvmppc_update_timing_stats(vcpu); | ||||
| 
 | ||||
| 	switch (exit_nr) { | ||||
| 	case BOOKE_INTERRUPT_EXTERNAL: | ||||
| 		do_IRQ(current->thread.regs); | ||||
| 		break; | ||||
| 
 | ||||
| 	case BOOKE_INTERRUPT_DECREMENTER: | ||||
| 		timer_interrupt(current->thread.regs); | ||||
| 		break; | ||||
| 
 | ||||
| #if defined(CONFIG_PPC_FSL_BOOK3E) || defined(CONFIG_PPC_BOOK3E_64) | ||||
| 	case BOOKE_INTERRUPT_DOORBELL: | ||||
| 		doorbell_exception(current->thread.regs); | ||||
| 		break; | ||||
| #endif | ||||
| 	case BOOKE_INTERRUPT_MACHINE_CHECK: | ||||
| 		/* FIXME */ | ||||
| 		break; | ||||
| 	} | ||||
| 
 | ||||
| 	local_irq_enable(); | ||||
| 
 | ||||
| 	run->exit_reason = KVM_EXIT_UNKNOWN; | ||||
|  | @ -384,30 +558,56 @@ int kvmppc_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu, | |||
| 
 | ||||
| 	switch (exit_nr) { | ||||
| 	case BOOKE_INTERRUPT_MACHINE_CHECK: | ||||
| 		printk("MACHINE CHECK: %lx\n", mfspr(SPRN_MCSR)); | ||||
| 		kvmppc_dump_vcpu(vcpu); | ||||
| 		r = RESUME_HOST; | ||||
| 		kvm_resched(vcpu); | ||||
| 		r = RESUME_GUEST; | ||||
| 		break; | ||||
| 
 | ||||
| 	case BOOKE_INTERRUPT_EXTERNAL: | ||||
| 		kvmppc_account_exit(vcpu, EXT_INTR_EXITS); | ||||
| 		if (need_resched()) | ||||
| 			cond_resched(); | ||||
| 		kvm_resched(vcpu); | ||||
| 		r = RESUME_GUEST; | ||||
| 		break; | ||||
| 
 | ||||
| 	case BOOKE_INTERRUPT_DECREMENTER: | ||||
| 		/* Since we switched IVPR back to the host's value, the host
 | ||||
| 		 * handled this interrupt the moment we enabled interrupts. | ||||
| 		 * Now we just offer it a chance to reschedule the guest. */ | ||||
| 		kvmppc_account_exit(vcpu, DEC_EXITS); | ||||
| 		if (need_resched()) | ||||
| 			cond_resched(); | ||||
| 		kvm_resched(vcpu); | ||||
| 		r = RESUME_GUEST; | ||||
| 		break; | ||||
| 
 | ||||
| 	case BOOKE_INTERRUPT_DOORBELL: | ||||
| 		kvmppc_account_exit(vcpu, DBELL_EXITS); | ||||
| 		kvm_resched(vcpu); | ||||
| 		r = RESUME_GUEST; | ||||
| 		break; | ||||
| 
 | ||||
| 	case BOOKE_INTERRUPT_GUEST_DBELL_CRIT: | ||||
| 		kvmppc_account_exit(vcpu, GDBELL_EXITS); | ||||
| 
 | ||||
| 		/*
 | ||||
| 		 * We are here because there is a pending guest interrupt | ||||
| 		 * which could not be delivered as MSR_CE or MSR_ME was not | ||||
| 		 * set.  Once we break from here we will retry delivery. | ||||
| 		 */ | ||||
| 		r = RESUME_GUEST; | ||||
| 		break; | ||||
| 
 | ||||
| 	case BOOKE_INTERRUPT_GUEST_DBELL: | ||||
| 		kvmppc_account_exit(vcpu, GDBELL_EXITS); | ||||
| 
 | ||||
| 		/*
 | ||||
| 		 * We are here because there is a pending guest interrupt | ||||
| 		 * which could not be delivered as MSR_EE was not set.  Once | ||||
| 		 * we break from here we will retry delivery. | ||||
| 		 */ | ||||
| 		r = RESUME_GUEST; | ||||
| 		break; | ||||
| 
 | ||||
| 	case BOOKE_INTERRUPT_HV_PRIV: | ||||
| 		r = emulation_exit(run, vcpu); | ||||
| 		break; | ||||
| 
 | ||||
| 	case BOOKE_INTERRUPT_PROGRAM: | ||||
| 		if (vcpu->arch.shared->msr & MSR_PR) { | ||||
| 		if (vcpu->arch.shared->msr & (MSR_PR | MSR_GS)) { | ||||
| 			/* Program traps generated by user-level software must be handled
 | ||||
| 			 * by the guest kernel. */ | ||||
| 			kvmppc_core_queue_program(vcpu, vcpu->arch.fault_esr); | ||||
|  | @ -416,32 +616,7 @@ int kvmppc_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu, | |||
| 			break; | ||||
| 		} | ||||
| 
 | ||||
| 		er = kvmppc_emulate_instruction(run, vcpu); | ||||
| 		switch (er) { | ||||
| 		case EMULATE_DONE: | ||||
| 			/* don't overwrite subtypes, just account kvm_stats */ | ||||
| 			kvmppc_account_exit_stat(vcpu, EMULATED_INST_EXITS); | ||||
| 			/* Future optimization: only reload non-volatiles if
 | ||||
| 			 * they were actually modified by emulation. */ | ||||
| 			r = RESUME_GUEST_NV; | ||||
| 			break; | ||||
| 		case EMULATE_DO_DCR: | ||||
| 			run->exit_reason = KVM_EXIT_DCR; | ||||
| 			r = RESUME_HOST; | ||||
| 			break; | ||||
| 		case EMULATE_FAIL: | ||||
| 			/* XXX Deliver Program interrupt to guest. */ | ||||
| 			printk(KERN_CRIT "%s: emulation at %lx failed (%08x)\n", | ||||
| 			       __func__, vcpu->arch.pc, vcpu->arch.last_inst); | ||||
| 			/* For debugging, encode the failing instruction and
 | ||||
| 			 * report it to userspace. */ | ||||
| 			run->hw.hardware_exit_reason = ~0ULL << 32; | ||||
| 			run->hw.hardware_exit_reason |= vcpu->arch.last_inst; | ||||
| 			r = RESUME_HOST; | ||||
| 			break; | ||||
| 		default: | ||||
| 			BUG(); | ||||
| 		} | ||||
| 		r = emulation_exit(run, vcpu); | ||||
| 		break; | ||||
| 
 | ||||
| 	case BOOKE_INTERRUPT_FP_UNAVAIL: | ||||
|  | @ -506,6 +681,21 @@ int kvmppc_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu, | |||
| 		r = RESUME_GUEST; | ||||
| 		break; | ||||
| 
 | ||||
| #ifdef CONFIG_KVM_BOOKE_HV | ||||
| 	case BOOKE_INTERRUPT_HV_SYSCALL: | ||||
| 		if (!(vcpu->arch.shared->msr & MSR_PR)) { | ||||
| 			kvmppc_set_gpr(vcpu, 3, kvmppc_kvm_pv(vcpu)); | ||||
| 		} else { | ||||
| 			/*
 | ||||
| 			 * hcall from guest userspace -- send privileged | ||||
| 			 * instruction program check. | ||||
| 			 */ | ||||
| 			kvmppc_core_queue_program(vcpu, ESR_PPR); | ||||
| 		} | ||||
| 
 | ||||
| 		r = RESUME_GUEST; | ||||
| 		break; | ||||
| #else | ||||
| 	case BOOKE_INTERRUPT_SYSCALL: | ||||
| 		if (!(vcpu->arch.shared->msr & MSR_PR) && | ||||
| 		    (((u32)kvmppc_get_gpr(vcpu, 0)) == KVM_SC_MAGIC_R0)) { | ||||
|  | @ -519,6 +709,7 @@ int kvmppc_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu, | |||
| 		kvmppc_account_exit(vcpu, SYSCALL_EXITS); | ||||
| 		r = RESUME_GUEST; | ||||
| 		break; | ||||
| #endif | ||||
| 
 | ||||
| 	case BOOKE_INTERRUPT_DTLB_MISS: { | ||||
| 		unsigned long eaddr = vcpu->arch.fault_dear; | ||||
|  | @ -659,12 +850,15 @@ int kvm_arch_vcpu_setup(struct kvm_vcpu *vcpu) | |||
| 	int r; | ||||
| 
 | ||||
| 	vcpu->arch.pc = 0; | ||||
| 	vcpu->arch.shared->msr = 0; | ||||
| 	vcpu->arch.shadow_msr = MSR_USER | MSR_DE | MSR_IS | MSR_DS; | ||||
| 	vcpu->arch.shared->pir = vcpu->vcpu_id; | ||||
| 	kvmppc_set_gpr(vcpu, 1, (16<<20) - 8); /* -8 for the callee-save LR slot */ | ||||
| 	kvmppc_set_msr(vcpu, 0); | ||||
| 
 | ||||
| #ifndef CONFIG_KVM_BOOKE_HV | ||||
| 	vcpu->arch.shadow_msr = MSR_USER | MSR_DE | MSR_IS | MSR_DS; | ||||
| 	vcpu->arch.shadow_pid = 1; | ||||
| 	vcpu->arch.shared->msr = 0; | ||||
| #endif | ||||
| 
 | ||||
| 	/* Eye-catching numbers so we know if the guest takes an interrupt
 | ||||
| 	 * before it's programmed its own IVPR/IVORs. */ | ||||
|  | @ -745,8 +939,8 @@ static void get_sregs_base(struct kvm_vcpu *vcpu, | |||
| 	sregs->u.e.csrr0 = vcpu->arch.csrr0; | ||||
| 	sregs->u.e.csrr1 = vcpu->arch.csrr1; | ||||
| 	sregs->u.e.mcsr = vcpu->arch.mcsr; | ||||
| 	sregs->u.e.esr = vcpu->arch.shared->esr; | ||||
| 	sregs->u.e.dear = vcpu->arch.shared->dar; | ||||
| 	sregs->u.e.esr = get_guest_esr(vcpu); | ||||
| 	sregs->u.e.dear = get_guest_dear(vcpu); | ||||
| 	sregs->u.e.tsr = vcpu->arch.tsr; | ||||
| 	sregs->u.e.tcr = vcpu->arch.tcr; | ||||
| 	sregs->u.e.dec = kvmppc_get_dec(vcpu, tb); | ||||
|  | @ -763,8 +957,8 @@ static int set_sregs_base(struct kvm_vcpu *vcpu, | |||
| 	vcpu->arch.csrr0 = sregs->u.e.csrr0; | ||||
| 	vcpu->arch.csrr1 = sregs->u.e.csrr1; | ||||
| 	vcpu->arch.mcsr = sregs->u.e.mcsr; | ||||
| 	vcpu->arch.shared->esr = sregs->u.e.esr; | ||||
| 	vcpu->arch.shared->dar = sregs->u.e.dear; | ||||
| 	set_guest_esr(vcpu, sregs->u.e.esr); | ||||
| 	set_guest_dear(vcpu, sregs->u.e.dear); | ||||
| 	vcpu->arch.vrsave = sregs->u.e.vrsave; | ||||
| 	kvmppc_set_tcr(vcpu, sregs->u.e.tcr); | ||||
| 
 | ||||
|  | @ -961,14 +1155,17 @@ void kvmppc_decrementer_func(unsigned long data) | |||
| 
 | ||||
| void kvmppc_booke_vcpu_load(struct kvm_vcpu *vcpu, int cpu) | ||||
| { | ||||
| 	current->thread.kvm_vcpu = vcpu; | ||||
| } | ||||
| 
 | ||||
| void kvmppc_booke_vcpu_put(struct kvm_vcpu *vcpu) | ||||
| { | ||||
| 	current->thread.kvm_vcpu = NULL; | ||||
| } | ||||
| 
 | ||||
| int __init kvmppc_booke_init(void) | ||||
| { | ||||
| #ifndef CONFIG_KVM_BOOKE_HV | ||||
| 	unsigned long ivor[16]; | ||||
| 	unsigned long max_ivor = 0; | ||||
| 	int i; | ||||
|  | @ -1011,7 +1208,7 @@ int __init kvmppc_booke_init(void) | |||
| 	} | ||||
| 	flush_icache_range(kvmppc_booke_handlers, | ||||
| 	                   kvmppc_booke_handlers + max_ivor + kvmppc_handler_len); | ||||
| 
 | ||||
| #endif /* !BOOKE_HV */ | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -48,7 +48,20 @@ | |||
| #define BOOKE_IRQPRIO_PERFORMANCE_MONITOR 19 | ||||
| /* Internal pseudo-irqprio for level triggered externals */ | ||||
| #define BOOKE_IRQPRIO_EXTERNAL_LEVEL 20 | ||||
| #define BOOKE_IRQPRIO_MAX 20 | ||||
| #define BOOKE_IRQPRIO_DBELL 21 | ||||
| #define BOOKE_IRQPRIO_DBELL_CRIT 22 | ||||
| #define BOOKE_IRQPRIO_MAX 23 | ||||
| 
 | ||||
| #define BOOKE_IRQMASK_EE ((1 << BOOKE_IRQPRIO_EXTERNAL_LEVEL) | \ | ||||
| 			  (1 << BOOKE_IRQPRIO_PERFORMANCE_MONITOR) | \ | ||||
| 			  (1 << BOOKE_IRQPRIO_DBELL) | \ | ||||
| 			  (1 << BOOKE_IRQPRIO_DECREMENTER) | \ | ||||
| 			  (1 << BOOKE_IRQPRIO_FIT) | \ | ||||
| 			  (1 << BOOKE_IRQPRIO_EXTERNAL)) | ||||
| 
 | ||||
| #define BOOKE_IRQMASK_CE ((1 << BOOKE_IRQPRIO_DBELL_CRIT) | \ | ||||
| 			  (1 << BOOKE_IRQPRIO_WATCHDOG) | \ | ||||
| 			  (1 << BOOKE_IRQPRIO_CRITICAL)) | ||||
| 
 | ||||
| extern unsigned long kvmppc_booke_handlers; | ||||
| 
 | ||||
|  | @ -74,4 +87,13 @@ void kvmppc_vcpu_disable_spe(struct kvm_vcpu *vcpu); | |||
| void kvmppc_booke_vcpu_load(struct kvm_vcpu *vcpu, int cpu); | ||||
| void kvmppc_booke_vcpu_put(struct kvm_vcpu *vcpu); | ||||
| 
 | ||||
| enum int_class { | ||||
| 	INT_CLASS_NONCRIT, | ||||
| 	INT_CLASS_CRIT, | ||||
| 	INT_CLASS_MC, | ||||
| 	INT_CLASS_DBG, | ||||
| }; | ||||
| 
 | ||||
| void kvmppc_set_pending_interrupt(struct kvm_vcpu *vcpu, enum int_class type); | ||||
| 
 | ||||
| #endif /* __KVM_BOOKE_H__ */ | ||||
|  |  | |||
|  | @ -99,6 +99,12 @@ int kvmppc_booke_emulate_op(struct kvm_run *run, struct kvm_vcpu *vcpu, | |||
| 	return emulated; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * NOTE: some of these registers are not emulated on BOOKE_HV (GS-mode). | ||||
|  * Their backing store is in real registers, and these functions | ||||
|  * will return the wrong result if called for them in another context | ||||
|  * (such as debugging). | ||||
|  */ | ||||
| int kvmppc_booke_emulate_mtspr(struct kvm_vcpu *vcpu, int sprn, int rs) | ||||
| { | ||||
| 	int emulated = EMULATE_DONE; | ||||
|  | @ -122,9 +128,11 @@ int kvmppc_booke_emulate_mtspr(struct kvm_vcpu *vcpu, int sprn, int rs) | |||
| 		kvmppc_set_tcr(vcpu, spr_val); | ||||
| 		break; | ||||
| 
 | ||||
| 	/* Note: SPRG4-7 are user-readable. These values are
 | ||||
| 	 * loaded into the real SPRGs when resuming the | ||||
| 	 * guest. */ | ||||
| 	/*
 | ||||
| 	 * Note: SPRG4-7 are user-readable. | ||||
| 	 * These values are loaded into the real SPRGs when resuming the | ||||
| 	 * guest (PR-mode only). | ||||
| 	 */ | ||||
| 	case SPRN_SPRG4: | ||||
| 		vcpu->arch.shared->sprg4 = spr_val; break; | ||||
| 	case SPRN_SPRG5: | ||||
|  | @ -136,6 +144,9 @@ int kvmppc_booke_emulate_mtspr(struct kvm_vcpu *vcpu, int sprn, int rs) | |||
| 
 | ||||
| 	case SPRN_IVPR: | ||||
| 		vcpu->arch.ivpr = spr_val; | ||||
| #ifdef CONFIG_KVM_BOOKE_HV | ||||
| 		mtspr(SPRN_GIVPR, spr_val); | ||||
| #endif | ||||
| 		break; | ||||
| 	case SPRN_IVOR0: | ||||
| 		vcpu->arch.ivor[BOOKE_IRQPRIO_CRITICAL] = spr_val; | ||||
|  | @ -145,6 +156,9 @@ int kvmppc_booke_emulate_mtspr(struct kvm_vcpu *vcpu, int sprn, int rs) | |||
| 		break; | ||||
| 	case SPRN_IVOR2: | ||||
| 		vcpu->arch.ivor[BOOKE_IRQPRIO_DATA_STORAGE] = spr_val; | ||||
| #ifdef CONFIG_KVM_BOOKE_HV | ||||
| 		mtspr(SPRN_GIVOR2, spr_val); | ||||
| #endif | ||||
| 		break; | ||||
| 	case SPRN_IVOR3: | ||||
| 		vcpu->arch.ivor[BOOKE_IRQPRIO_INST_STORAGE] = spr_val; | ||||
|  | @ -163,6 +177,9 @@ int kvmppc_booke_emulate_mtspr(struct kvm_vcpu *vcpu, int sprn, int rs) | |||
| 		break; | ||||
| 	case SPRN_IVOR8: | ||||
| 		vcpu->arch.ivor[BOOKE_IRQPRIO_SYSCALL] = spr_val; | ||||
| #ifdef CONFIG_KVM_BOOKE_HV | ||||
| 		mtspr(SPRN_GIVOR8, spr_val); | ||||
| #endif | ||||
| 		break; | ||||
| 	case SPRN_IVOR9: | ||||
| 		vcpu->arch.ivor[BOOKE_IRQPRIO_AP_UNAVAIL] = spr_val; | ||||
|  |  | |||
							
								
								
									
										587
									
								
								arch/powerpc/kvm/bookehv_interrupts.S
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										587
									
								
								arch/powerpc/kvm/bookehv_interrupts.S
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,587 @@ | |||
| /* | ||||
|  * This program is free software; you can redistribute it and/or modify
 | ||||
|  * it under the terms of the GNU General Public License, version 2, as | ||||
|  * published by the Free Software Foundation. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program; if not, write to the Free Software
 | ||||
|  * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. | ||||
|  * | ||||
|  * Copyright (C) 2010-2011 Freescale Semiconductor, Inc. | ||||
|  * | ||||
|  * Author: Varun Sethi <varun.sethi@freescale.com>
 | ||||
|  * Author: Scott Wood <scotwood@freescale.com>
 | ||||
|  * | ||||
|  * This file is derived from arch/powerpc/kvm/booke_interrupts.S | ||||
|  */ | ||||
| 
 | ||||
| #include <asm/ppc_asm.h> | ||||
| #include <asm/kvm_asm.h> | ||||
| #include <asm/reg.h> | ||||
| #include <asm/mmu-44x.h> | ||||
| #include <asm/page.h> | ||||
| #include <asm/asm-compat.h> | ||||
| #include <asm/asm-offsets.h> | ||||
| #include <asm/bitsperlong.h> | ||||
| 
 | ||||
| #include "../kernel/head_booke.h" /* for THREAD_NORMSAVE() */ | ||||
| 
 | ||||
| #define GET_VCPU(vcpu, thread)	\ | ||||
| 	PPC_LL	vcpu, THREAD_KVM_VCPU(thread) | ||||
| 
 | ||||
| #define SET_VCPU(vcpu)		\ | ||||
|         PPC_STL	vcpu, (THREAD + THREAD_KVM_VCPU)(r2) | ||||
| 
 | ||||
| #define LONGBYTES		(BITS_PER_LONG / 8) | ||||
| 
 | ||||
| #define VCPU_GPR(n)     	(VCPU_GPRS + (n * LONGBYTES)) | ||||
| #define VCPU_GUEST_SPRG(n)	(VCPU_GUEST_SPRGS + (n * LONGBYTES)) | ||||
| 
 | ||||
| /* The host stack layout: */ | ||||
| #define HOST_R1         (0 * LONGBYTES) /* Implied by stwu. */ | ||||
| #define HOST_CALLEE_LR  (1 * LONGBYTES) | ||||
| #define HOST_RUN        (2 * LONGBYTES) /* struct kvm_run */ | ||||
| /* | ||||
|  * r2 is special: it holds 'current', and it made nonvolatile in the | ||||
|  * kernel with the -ffixed-r2 gcc option. | ||||
|  */ | ||||
| #define HOST_R2         (3 * LONGBYTES) | ||||
| #define HOST_NV_GPRS    (4 * LONGBYTES) | ||||
| #define HOST_NV_GPR(n)  (HOST_NV_GPRS + ((n - 14) * LONGBYTES)) | ||||
| #define HOST_MIN_STACK_SIZE (HOST_NV_GPR(31) + LONGBYTES) | ||||
| #define HOST_STACK_SIZE ((HOST_MIN_STACK_SIZE + 15) & ~15) /* Align. */ | ||||
| #define HOST_STACK_LR   (HOST_STACK_SIZE + LONGBYTES) /* In caller stack frame. */ | ||||
| 
 | ||||
| #define NEED_EMU		0x00000001 /* emulation -- save nv regs */ | ||||
| #define NEED_DEAR		0x00000002 /* save faulting DEAR */ | ||||
| #define NEED_ESR		0x00000004 /* save faulting ESR */ | ||||
| 
 | ||||
| /* | ||||
|  * On entry: | ||||
|  * r4 = vcpu, r5 = srr0, r6 = srr1 | ||||
|  * saved in vcpu: cr, ctr, r3-r13 | ||||
|  */ | ||||
| .macro kvm_handler_common intno, srr0, flags | ||||
| 	mfspr	r10, SPRN_PID | ||||
| 	lwz	r8, VCPU_HOST_PID(r4) | ||||
| 	PPC_LL	r11, VCPU_SHARED(r4) | ||||
| 	PPC_STL	r14, VCPU_GPR(r14)(r4) /* We need a non-volatile GPR. */ | ||||
| 	li	r14, \intno | ||||
| 
 | ||||
| 	stw	r10, VCPU_GUEST_PID(r4) | ||||
| 	mtspr	SPRN_PID, r8 | ||||
| 
 | ||||
| 	.if	\flags & NEED_EMU | ||||
| 	lwz	r9, VCPU_KVM(r4) | ||||
| 	.endif | ||||
| 
 | ||||
| #ifdef CONFIG_KVM_EXIT_TIMING | ||||
| 	/* save exit time */ | ||||
| 1:	mfspr	r7, SPRN_TBRU | ||||
| 	mfspr	r8, SPRN_TBRL | ||||
| 	mfspr	r9, SPRN_TBRU | ||||
| 	cmpw	r9, r7 | ||||
| 	PPC_STL	r8, VCPU_TIMING_EXIT_TBL(r4) | ||||
| 	bne-	1b | ||||
| 	PPC_STL	r9, VCPU_TIMING_EXIT_TBU(r4) | ||||
| #endif | ||||
| 
 | ||||
| 	oris	r8, r6, MSR_CE@h
 | ||||
| #ifndef CONFIG_64BIT | ||||
| 	stw	r6, (VCPU_SHARED_MSR + 4)(r11) | ||||
| #else | ||||
| 	std	r6, (VCPU_SHARED_MSR)(r11) | ||||
| #endif | ||||
| 	ori	r8, r8, MSR_ME | MSR_RI | ||||
| 	PPC_STL	r5, VCPU_PC(r4) | ||||
| 
 | ||||
| 	/* | ||||
| 	 * Make sure CE/ME/RI are set (if appropriate for exception type) | ||||
| 	 * whether or not the guest had it set.  Since mfmsr/mtmsr are | ||||
| 	 * somewhat expensive, skip in the common case where the guest | ||||
| 	 * had all these bits set (and thus they're still set if | ||||
| 	 * appropriate for the exception type). | ||||
| 	 */ | ||||
| 	cmpw	r6, r8 | ||||
| 	.if	\flags & NEED_EMU | ||||
| 	lwz	r9, KVM_LPID(r9) | ||||
| 	.endif | ||||
| 	beq	1f | ||||
| 	mfmsr	r7 | ||||
| 	.if	\srr0 != SPRN_MCSRR0 && \srr0 != SPRN_CSRR0 | ||||
| 	oris	r7, r7, MSR_CE@h
 | ||||
| 	.endif | ||||
| 	.if	\srr0 != SPRN_MCSRR0 | ||||
| 	ori	r7, r7, MSR_ME | MSR_RI | ||||
| 	.endif | ||||
| 	mtmsr	r7 | ||||
| 1: | ||||
| 
 | ||||
| 	.if	\flags & NEED_EMU | ||||
| 	/* | ||||
| 	 * This assumes you have external PID support. | ||||
| 	 * To support a bookehv CPU without external PID, you'll | ||||
| 	 * need to look up the TLB entry and create a temporary mapping. | ||||
| 	 * | ||||
| 	 * FIXME: we don't currently handle if the lwepx faults.  PR-mode | ||||
| 	 * booke doesn't handle it either.  Since Linux doesn't use | ||||
| 	 * broadcast tlbivax anymore, the only way this should happen is | ||||
| 	 * if the guest maps its memory execute-but-not-read, or if we | ||||
| 	 * somehow take a TLB miss in the middle of this entry code and | ||||
| 	 * evict the relevant entry.  On e500mc, all kernel lowmem is | ||||
| 	 * bolted into TLB1 large page mappings, and we don't use | ||||
| 	 * broadcast invalidates, so we should not take a TLB miss here. | ||||
| 	 * | ||||
| 	 * Later we'll need to deal with faults here.  Disallowing guest | ||||
| 	 * mappings that are execute-but-not-read could be an option on | ||||
| 	 * e500mc, but not on chips with an LRAT if it is used. | ||||
| 	 */ | ||||
| 
 | ||||
| 	mfspr	r3, SPRN_EPLC	/* will already have correct ELPID and EGS */ | ||||
| 	PPC_STL	r15, VCPU_GPR(r15)(r4) | ||||
| 	PPC_STL	r16, VCPU_GPR(r16)(r4) | ||||
| 	PPC_STL	r17, VCPU_GPR(r17)(r4) | ||||
| 	PPC_STL	r18, VCPU_GPR(r18)(r4) | ||||
| 	PPC_STL	r19, VCPU_GPR(r19)(r4) | ||||
| 	mr	r8, r3 | ||||
| 	PPC_STL	r20, VCPU_GPR(r20)(r4) | ||||
| 	rlwimi	r8, r6, EPC_EAS_SHIFT - MSR_IR_LG, EPC_EAS | ||||
| 	PPC_STL	r21, VCPU_GPR(r21)(r4) | ||||
| 	rlwimi	r8, r6, EPC_EPR_SHIFT - MSR_PR_LG, EPC_EPR | ||||
| 	PPC_STL	r22, VCPU_GPR(r22)(r4) | ||||
| 	rlwimi	r8, r10, EPC_EPID_SHIFT, EPC_EPID | ||||
| 	PPC_STL	r23, VCPU_GPR(r23)(r4) | ||||
| 	PPC_STL	r24, VCPU_GPR(r24)(r4) | ||||
| 	PPC_STL	r25, VCPU_GPR(r25)(r4) | ||||
| 	PPC_STL	r26, VCPU_GPR(r26)(r4) | ||||
| 	PPC_STL	r27, VCPU_GPR(r27)(r4) | ||||
| 	PPC_STL	r28, VCPU_GPR(r28)(r4) | ||||
| 	PPC_STL	r29, VCPU_GPR(r29)(r4) | ||||
| 	PPC_STL	r30, VCPU_GPR(r30)(r4) | ||||
| 	PPC_STL	r31, VCPU_GPR(r31)(r4) | ||||
| 	mtspr	SPRN_EPLC, r8 | ||||
| 	isync | ||||
| 	lwepx	r9, 0, r5 | ||||
| 	mtspr	SPRN_EPLC, r3 | ||||
| 	stw	r9, VCPU_LAST_INST(r4) | ||||
| 	.endif | ||||
| 
 | ||||
| 	.if	\flags & NEED_ESR | ||||
| 	mfspr	r8, SPRN_ESR | ||||
| 	PPC_STL	r8, VCPU_FAULT_ESR(r4) | ||||
| 	.endif | ||||
| 
 | ||||
| 	.if	\flags & NEED_DEAR | ||||
| 	mfspr	r9, SPRN_DEAR | ||||
| 	PPC_STL	r9, VCPU_FAULT_DEAR(r4) | ||||
| 	.endif | ||||
| 
 | ||||
| 	b	kvmppc_resume_host | ||||
| .endm | ||||
| 
 | ||||
| /* | ||||
|  * For input register values, see arch/powerpc/include/asm/kvm_booke_hv_asm.h | ||||
|  */ | ||||
| .macro kvm_handler intno srr0, srr1, flags | ||||
| _GLOBAL(kvmppc_handler_\intno\()_\srr1) | ||||
| 	GET_VCPU(r11, r10) | ||||
| 	PPC_STL r3, VCPU_GPR(r3)(r11) | ||||
| 	mfspr	r3, SPRN_SPRG_RSCRATCH0 | ||||
| 	PPC_STL	r4, VCPU_GPR(r4)(r11) | ||||
| 	PPC_LL	r4, THREAD_NORMSAVE(0)(r10) | ||||
| 	PPC_STL	r5, VCPU_GPR(r5)(r11) | ||||
| 	PPC_STL	r13, VCPU_CR(r11) | ||||
| 	mfspr	r5, \srr0 | ||||
| 	PPC_STL	r3, VCPU_GPR(r10)(r11) | ||||
| 	PPC_LL	r3, THREAD_NORMSAVE(2)(r10) | ||||
| 	PPC_STL	r6, VCPU_GPR(r6)(r11) | ||||
| 	PPC_STL	r4, VCPU_GPR(r11)(r11) | ||||
| 	mfspr	r6, \srr1 | ||||
| 	PPC_STL	r7, VCPU_GPR(r7)(r11) | ||||
| 	PPC_STL	r8, VCPU_GPR(r8)(r11) | ||||
| 	PPC_STL	r9, VCPU_GPR(r9)(r11) | ||||
| 	PPC_STL r3, VCPU_GPR(r13)(r11) | ||||
| 	mfctr	r7 | ||||
| 	PPC_STL	r12, VCPU_GPR(r12)(r11) | ||||
| 	PPC_STL	r7, VCPU_CTR(r11) | ||||
| 	mr	r4, r11 | ||||
| 	kvm_handler_common \intno, \srr0, \flags | ||||
| .endm | ||||
| 
 | ||||
| .macro kvm_lvl_handler intno scratch srr0, srr1, flags | ||||
| _GLOBAL(kvmppc_handler_\intno\()_\srr1) | ||||
| 	mfspr	r10, SPRN_SPRG_THREAD | ||||
| 	GET_VCPU(r11, r10) | ||||
| 	PPC_STL r3, VCPU_GPR(r3)(r11) | ||||
| 	mfspr	r3, \scratch | ||||
| 	PPC_STL	r4, VCPU_GPR(r4)(r11) | ||||
| 	PPC_LL	r4, GPR9(r8) | ||||
| 	PPC_STL	r5, VCPU_GPR(r5)(r11) | ||||
| 	PPC_STL	r9, VCPU_CR(r11) | ||||
| 	mfspr	r5, \srr0 | ||||
| 	PPC_STL	r3, VCPU_GPR(r8)(r11) | ||||
| 	PPC_LL	r3, GPR10(r8) | ||||
| 	PPC_STL	r6, VCPU_GPR(r6)(r11) | ||||
| 	PPC_STL	r4, VCPU_GPR(r9)(r11) | ||||
| 	mfspr	r6, \srr1 | ||||
| 	PPC_LL	r4, GPR11(r8) | ||||
| 	PPC_STL	r7, VCPU_GPR(r7)(r11) | ||||
| 	PPC_STL	r8, VCPU_GPR(r8)(r11) | ||||
| 	PPC_STL r3, VCPU_GPR(r10)(r11) | ||||
| 	mfctr	r7 | ||||
| 	PPC_STL	r12, VCPU_GPR(r12)(r11) | ||||
| 	PPC_STL	r4, VCPU_GPR(r11)(r11) | ||||
| 	PPC_STL	r7, VCPU_CTR(r11) | ||||
| 	mr	r4, r11 | ||||
| 	kvm_handler_common \intno, \srr0, \flags | ||||
| .endm | ||||
| 
 | ||||
| kvm_lvl_handler BOOKE_INTERRUPT_CRITICAL, \ | ||||
| 	SPRN_SPRG_RSCRATCH_CRIT, SPRN_CSRR0, SPRN_CSRR1, 0 | ||||
| kvm_lvl_handler BOOKE_INTERRUPT_MACHINE_CHECK, \ | ||||
| 	SPRN_SPRG_RSCRATCH_MC, SPRN_MCSRR0, SPRN_MCSRR1, 0 | ||||
| kvm_handler BOOKE_INTERRUPT_DATA_STORAGE, \ | ||||
| 	SPRN_SRR0, SPRN_SRR1, (NEED_EMU | NEED_DEAR) | ||||
| kvm_handler BOOKE_INTERRUPT_INST_STORAGE, SPRN_SRR0, SPRN_SRR1, NEED_ESR | ||||
| kvm_handler BOOKE_INTERRUPT_EXTERNAL, SPRN_SRR0, SPRN_SRR1, 0 | ||||
| kvm_handler BOOKE_INTERRUPT_ALIGNMENT, \ | ||||
| 	SPRN_SRR0, SPRN_SRR1, (NEED_DEAR | NEED_ESR) | ||||
| kvm_handler BOOKE_INTERRUPT_PROGRAM, SPRN_SRR0, SPRN_SRR1, NEED_ESR | ||||
| kvm_handler BOOKE_INTERRUPT_FP_UNAVAIL, SPRN_SRR0, SPRN_SRR1, 0 | ||||
| kvm_handler BOOKE_INTERRUPT_SYSCALL, SPRN_SRR0, SPRN_SRR1, 0 | ||||
| kvm_handler BOOKE_INTERRUPT_AP_UNAVAIL, SPRN_SRR0, SPRN_SRR1, 0 | ||||
| kvm_handler BOOKE_INTERRUPT_DECREMENTER, SPRN_SRR0, SPRN_SRR1, 0 | ||||
| kvm_handler BOOKE_INTERRUPT_FIT, SPRN_SRR0, SPRN_SRR1, 0 | ||||
| kvm_lvl_handler BOOKE_INTERRUPT_WATCHDOG, \ | ||||
| 	SPRN_SPRG_RSCRATCH_CRIT, SPRN_CSRR0, SPRN_CSRR1, 0 | ||||
| kvm_handler BOOKE_INTERRUPT_DTLB_MISS, \ | ||||
| 	SPRN_SRR0, SPRN_SRR1, (NEED_EMU | NEED_DEAR | NEED_ESR) | ||||
| kvm_handler BOOKE_INTERRUPT_ITLB_MISS, SPRN_SRR0, SPRN_SRR1, 0 | ||||
| kvm_handler BOOKE_INTERRUPT_SPE_UNAVAIL, SPRN_SRR0, SPRN_SRR1, 0 | ||||
| kvm_handler BOOKE_INTERRUPT_SPE_FP_DATA, SPRN_SRR0, SPRN_SRR1, 0 | ||||
| kvm_handler BOOKE_INTERRUPT_SPE_FP_ROUND, SPRN_SRR0, SPRN_SRR1, 0 | ||||
| kvm_handler BOOKE_INTERRUPT_PERFORMANCE_MONITOR, SPRN_SRR0, SPRN_SRR1, 0 | ||||
| kvm_handler BOOKE_INTERRUPT_DOORBELL, SPRN_SRR0, SPRN_SRR1, 0 | ||||
| kvm_lvl_handler BOOKE_INTERRUPT_DOORBELL_CRITICAL, \ | ||||
| 	SPRN_SPRG_RSCRATCH_CRIT, SPRN_CSRR0, SPRN_CSRR1, 0 | ||||
| kvm_handler BOOKE_INTERRUPT_HV_PRIV, SPRN_SRR0, SPRN_SRR1, NEED_EMU | ||||
| kvm_handler BOOKE_INTERRUPT_HV_SYSCALL, SPRN_SRR0, SPRN_SRR1, 0 | ||||
| kvm_handler BOOKE_INTERRUPT_GUEST_DBELL, SPRN_GSRR0, SPRN_GSRR1, 0 | ||||
| kvm_lvl_handler BOOKE_INTERRUPT_GUEST_DBELL_CRIT, \ | ||||
| 	SPRN_SPRG_RSCRATCH_CRIT, SPRN_CSRR0, SPRN_CSRR1, 0 | ||||
| kvm_lvl_handler BOOKE_INTERRUPT_DEBUG, \ | ||||
| 	SPRN_SPRG_RSCRATCH_CRIT, SPRN_CSRR0, SPRN_CSRR1, 0 | ||||
| kvm_lvl_handler BOOKE_INTERRUPT_DEBUG, \ | ||||
| 	SPRN_SPRG_RSCRATCH_DBG, SPRN_DSRR0, SPRN_DSRR1, 0 | ||||
| 
 | ||||
| 
 | ||||
| /* Registers: | ||||
|  *  SPRG_SCRATCH0: guest r10 | ||||
|  *  r4: vcpu pointer | ||||
|  *  r11: vcpu->arch.shared | ||||
|  *  r14: KVM exit number | ||||
|  */ | ||||
| _GLOBAL(kvmppc_resume_host) | ||||
| 	/* Save remaining volatile guest register state to vcpu. */ | ||||
| 	mfspr	r3, SPRN_VRSAVE | ||||
| 	PPC_STL	r0, VCPU_GPR(r0)(r4) | ||||
| 	PPC_STL	r1, VCPU_GPR(r1)(r4) | ||||
| 	mflr	r5 | ||||
| 	mfspr	r6, SPRN_SPRG4 | ||||
| 	PPC_STL	r2, VCPU_GPR(r2)(r4) | ||||
| 	PPC_STL	r5, VCPU_LR(r4) | ||||
| 	mfspr	r7, SPRN_SPRG5 | ||||
| 	PPC_STL	r3, VCPU_VRSAVE(r4) | ||||
| 	PPC_STL	r6, VCPU_SHARED_SPRG4(r11) | ||||
| 	mfspr	r8, SPRN_SPRG6 | ||||
| 	PPC_STL	r7, VCPU_SHARED_SPRG5(r11) | ||||
| 	mfspr	r9, SPRN_SPRG7 | ||||
| 	PPC_STL	r8, VCPU_SHARED_SPRG6(r11) | ||||
| 	mfxer	r3 | ||||
| 	PPC_STL	r9, VCPU_SHARED_SPRG7(r11) | ||||
| 
 | ||||
| 	/* save guest MAS registers and restore host mas4 & mas6 */ | ||||
| 	mfspr	r5, SPRN_MAS0 | ||||
| 	PPC_STL	r3, VCPU_XER(r4) | ||||
| 	mfspr	r6, SPRN_MAS1 | ||||
| 	stw	r5, VCPU_SHARED_MAS0(r11) | ||||
| 	mfspr	r7, SPRN_MAS2 | ||||
| 	stw	r6, VCPU_SHARED_MAS1(r11) | ||||
| #ifndef CONFIG_64BIT | ||||
| 	stw	r7, (VCPU_SHARED_MAS2 + 4)(r11) | ||||
| #else | ||||
| 	std	r7, (VCPU_SHARED_MAS2)(r11) | ||||
| #endif | ||||
| 	mfspr	r5, SPRN_MAS3 | ||||
| 	mfspr	r6, SPRN_MAS4 | ||||
| 	stw	r5, VCPU_SHARED_MAS7_3+4(r11) | ||||
| 	mfspr	r7, SPRN_MAS6 | ||||
| 	stw	r6, VCPU_SHARED_MAS4(r11) | ||||
| 	mfspr	r5, SPRN_MAS7 | ||||
| 	lwz	r6, VCPU_HOST_MAS4(r4) | ||||
| 	stw	r7, VCPU_SHARED_MAS6(r11) | ||||
| 	lwz	r8, VCPU_HOST_MAS6(r4) | ||||
| 	mtspr	SPRN_MAS4, r6 | ||||
| 	stw	r5, VCPU_SHARED_MAS7_3+0(r11) | ||||
| 	mtspr	SPRN_MAS6, r8 | ||||
| 	mfspr	r3, SPRN_EPCR | ||||
| 	rlwinm	r3, r3, 0, ~SPRN_EPCR_DMIUH | ||||
| 	mtspr	SPRN_EPCR, r3 | ||||
| 	isync | ||||
| 
 | ||||
| 	/* Restore host stack pointer */ | ||||
| 	PPC_LL	r1, VCPU_HOST_STACK(r4) | ||||
| 	PPC_LL	r2, HOST_R2(r1) | ||||
| 
 | ||||
| 	/* Switch to kernel stack and jump to handler. */ | ||||
| 	PPC_LL	r3, HOST_RUN(r1) | ||||
| 	mr	r5, r14 /* intno */ | ||||
| 	mr	r14, r4 /* Save vcpu pointer. */ | ||||
| 	bl	kvmppc_handle_exit | ||||
| 
 | ||||
| 	/* Restore vcpu pointer and the nonvolatiles we used. */ | ||||
| 	mr	r4, r14 | ||||
| 	PPC_LL	r14, VCPU_GPR(r14)(r4) | ||||
| 
 | ||||
| 	andi.	r5, r3, RESUME_FLAG_NV | ||||
| 	beq	skip_nv_load | ||||
| 	PPC_LL	r15, VCPU_GPR(r15)(r4) | ||||
| 	PPC_LL	r16, VCPU_GPR(r16)(r4) | ||||
| 	PPC_LL	r17, VCPU_GPR(r17)(r4) | ||||
| 	PPC_LL	r18, VCPU_GPR(r18)(r4) | ||||
| 	PPC_LL	r19, VCPU_GPR(r19)(r4) | ||||
| 	PPC_LL	r20, VCPU_GPR(r20)(r4) | ||||
| 	PPC_LL	r21, VCPU_GPR(r21)(r4) | ||||
| 	PPC_LL	r22, VCPU_GPR(r22)(r4) | ||||
| 	PPC_LL	r23, VCPU_GPR(r23)(r4) | ||||
| 	PPC_LL	r24, VCPU_GPR(r24)(r4) | ||||
| 	PPC_LL	r25, VCPU_GPR(r25)(r4) | ||||
| 	PPC_LL	r26, VCPU_GPR(r26)(r4) | ||||
| 	PPC_LL	r27, VCPU_GPR(r27)(r4) | ||||
| 	PPC_LL	r28, VCPU_GPR(r28)(r4) | ||||
| 	PPC_LL	r29, VCPU_GPR(r29)(r4) | ||||
| 	PPC_LL	r30, VCPU_GPR(r30)(r4) | ||||
| 	PPC_LL	r31, VCPU_GPR(r31)(r4) | ||||
| skip_nv_load: | ||||
| 	/* Should we return to the guest? */ | ||||
| 	andi.	r5, r3, RESUME_FLAG_HOST | ||||
| 	beq	lightweight_exit | ||||
| 
 | ||||
| 	srawi	r3, r3, 2 /* Shift -ERR back down. */ | ||||
| 
 | ||||
| heavyweight_exit: | ||||
| 	/* Not returning to guest. */ | ||||
| 	PPC_LL	r5, HOST_STACK_LR(r1) | ||||
| 
 | ||||
| 	/* | ||||
| 	 * We already saved guest volatile register state; now save the
 | ||||
| 	 * non-volatiles. | ||||
| 	 */ | ||||
| 
 | ||||
| 	PPC_STL	r15, VCPU_GPR(r15)(r4) | ||||
| 	PPC_STL	r16, VCPU_GPR(r16)(r4) | ||||
| 	PPC_STL	r17, VCPU_GPR(r17)(r4) | ||||
| 	PPC_STL	r18, VCPU_GPR(r18)(r4) | ||||
| 	PPC_STL	r19, VCPU_GPR(r19)(r4) | ||||
| 	PPC_STL	r20, VCPU_GPR(r20)(r4) | ||||
| 	PPC_STL	r21, VCPU_GPR(r21)(r4) | ||||
| 	PPC_STL	r22, VCPU_GPR(r22)(r4) | ||||
| 	PPC_STL	r23, VCPU_GPR(r23)(r4) | ||||
| 	PPC_STL	r24, VCPU_GPR(r24)(r4) | ||||
| 	PPC_STL	r25, VCPU_GPR(r25)(r4) | ||||
| 	PPC_STL	r26, VCPU_GPR(r26)(r4) | ||||
| 	PPC_STL	r27, VCPU_GPR(r27)(r4) | ||||
| 	PPC_STL	r28, VCPU_GPR(r28)(r4) | ||||
| 	PPC_STL	r29, VCPU_GPR(r29)(r4) | ||||
| 	PPC_STL	r30, VCPU_GPR(r30)(r4) | ||||
| 	PPC_STL	r31, VCPU_GPR(r31)(r4) | ||||
| 
 | ||||
| 	/* Load host non-volatile register state from host stack. */ | ||||
| 	PPC_LL	r14, HOST_NV_GPR(r14)(r1) | ||||
| 	PPC_LL	r15, HOST_NV_GPR(r15)(r1) | ||||
| 	PPC_LL	r16, HOST_NV_GPR(r16)(r1) | ||||
| 	PPC_LL	r17, HOST_NV_GPR(r17)(r1) | ||||
| 	PPC_LL	r18, HOST_NV_GPR(r18)(r1) | ||||
| 	PPC_LL	r19, HOST_NV_GPR(r19)(r1) | ||||
| 	PPC_LL	r20, HOST_NV_GPR(r20)(r1) | ||||
| 	PPC_LL	r21, HOST_NV_GPR(r21)(r1) | ||||
| 	PPC_LL	r22, HOST_NV_GPR(r22)(r1) | ||||
| 	PPC_LL	r23, HOST_NV_GPR(r23)(r1) | ||||
| 	PPC_LL	r24, HOST_NV_GPR(r24)(r1) | ||||
| 	PPC_LL	r25, HOST_NV_GPR(r25)(r1) | ||||
| 	PPC_LL	r26, HOST_NV_GPR(r26)(r1) | ||||
| 	PPC_LL	r27, HOST_NV_GPR(r27)(r1) | ||||
| 	PPC_LL	r28, HOST_NV_GPR(r28)(r1) | ||||
| 	PPC_LL	r29, HOST_NV_GPR(r29)(r1) | ||||
| 	PPC_LL	r30, HOST_NV_GPR(r30)(r1) | ||||
| 	PPC_LL	r31, HOST_NV_GPR(r31)(r1) | ||||
| 
 | ||||
| 	/* Return to kvm_vcpu_run(). */ | ||||
| 	mtlr	r5 | ||||
| 	addi	r1, r1, HOST_STACK_SIZE | ||||
| 	/* r3 still contains the return code from kvmppc_handle_exit(). */ | ||||
| 	blr | ||||
| 
 | ||||
| /* Registers: | ||||
|  *  r3: kvm_run pointer | ||||
|  *  r4: vcpu pointer | ||||
|  */ | ||||
| _GLOBAL(__kvmppc_vcpu_run) | ||||
| 	stwu	r1, -HOST_STACK_SIZE(r1) | ||||
| 	PPC_STL	r1, VCPU_HOST_STACK(r4)	/* Save stack pointer to vcpu. */ | ||||
| 
 | ||||
| 	/* Save host state to stack. */ | ||||
| 	PPC_STL	r3, HOST_RUN(r1) | ||||
| 	mflr	r3 | ||||
| 	PPC_STL	r3, HOST_STACK_LR(r1) | ||||
| 
 | ||||
| 	/* Save host non-volatile register state to stack. */ | ||||
| 	PPC_STL	r14, HOST_NV_GPR(r14)(r1) | ||||
| 	PPC_STL	r15, HOST_NV_GPR(r15)(r1) | ||||
| 	PPC_STL	r16, HOST_NV_GPR(r16)(r1) | ||||
| 	PPC_STL	r17, HOST_NV_GPR(r17)(r1) | ||||
| 	PPC_STL	r18, HOST_NV_GPR(r18)(r1) | ||||
| 	PPC_STL	r19, HOST_NV_GPR(r19)(r1) | ||||
| 	PPC_STL	r20, HOST_NV_GPR(r20)(r1) | ||||
| 	PPC_STL	r21, HOST_NV_GPR(r21)(r1) | ||||
| 	PPC_STL	r22, HOST_NV_GPR(r22)(r1) | ||||
| 	PPC_STL	r23, HOST_NV_GPR(r23)(r1) | ||||
| 	PPC_STL	r24, HOST_NV_GPR(r24)(r1) | ||||
| 	PPC_STL	r25, HOST_NV_GPR(r25)(r1) | ||||
| 	PPC_STL	r26, HOST_NV_GPR(r26)(r1) | ||||
| 	PPC_STL	r27, HOST_NV_GPR(r27)(r1) | ||||
| 	PPC_STL	r28, HOST_NV_GPR(r28)(r1) | ||||
| 	PPC_STL	r29, HOST_NV_GPR(r29)(r1) | ||||
| 	PPC_STL	r30, HOST_NV_GPR(r30)(r1) | ||||
| 	PPC_STL	r31, HOST_NV_GPR(r31)(r1) | ||||
| 
 | ||||
| 	/* Load guest non-volatiles. */ | ||||
| 	PPC_LL	r14, VCPU_GPR(r14)(r4) | ||||
| 	PPC_LL	r15, VCPU_GPR(r15)(r4) | ||||
| 	PPC_LL	r16, VCPU_GPR(r16)(r4) | ||||
| 	PPC_LL	r17, VCPU_GPR(r17)(r4) | ||||
| 	PPC_LL	r18, VCPU_GPR(r18)(r4) | ||||
| 	PPC_LL	r19, VCPU_GPR(r19)(r4) | ||||
| 	PPC_LL	r20, VCPU_GPR(r20)(r4) | ||||
| 	PPC_LL	r21, VCPU_GPR(r21)(r4) | ||||
| 	PPC_LL	r22, VCPU_GPR(r22)(r4) | ||||
| 	PPC_LL	r23, VCPU_GPR(r23)(r4) | ||||
| 	PPC_LL	r24, VCPU_GPR(r24)(r4) | ||||
| 	PPC_LL	r25, VCPU_GPR(r25)(r4) | ||||
| 	PPC_LL	r26, VCPU_GPR(r26)(r4) | ||||
| 	PPC_LL	r27, VCPU_GPR(r27)(r4) | ||||
| 	PPC_LL	r28, VCPU_GPR(r28)(r4) | ||||
| 	PPC_LL	r29, VCPU_GPR(r29)(r4) | ||||
| 	PPC_LL	r30, VCPU_GPR(r30)(r4) | ||||
| 	PPC_LL	r31, VCPU_GPR(r31)(r4) | ||||
| 
 | ||||
| 
 | ||||
| lightweight_exit: | ||||
| 	PPC_STL	r2, HOST_R2(r1) | ||||
| 
 | ||||
| 	mfspr	r3, SPRN_PID | ||||
| 	stw	r3, VCPU_HOST_PID(r4) | ||||
| 	lwz	r3, VCPU_GUEST_PID(r4) | ||||
| 	mtspr	SPRN_PID, r3 | ||||
| 
 | ||||
| 	/* Save vcpu pointer for the exception handlers | ||||
| 	 * must be done before loading guest r2. | ||||
| 	 */ | ||||
| //	SET_VCPU(r4) | ||||
| 
 | ||||
| 	PPC_LL	r11, VCPU_SHARED(r4) | ||||
| 	/* Save host mas4 and mas6 and load guest MAS registers */ | ||||
| 	mfspr	r3, SPRN_MAS4 | ||||
| 	stw	r3, VCPU_HOST_MAS4(r4) | ||||
| 	mfspr	r3, SPRN_MAS6 | ||||
| 	stw	r3, VCPU_HOST_MAS6(r4) | ||||
| 	lwz	r3, VCPU_SHARED_MAS0(r11) | ||||
| 	lwz	r5, VCPU_SHARED_MAS1(r11) | ||||
| #ifndef CONFIG_64BIT | ||||
| 	lwz	r6, (VCPU_SHARED_MAS2 + 4)(r11) | ||||
| #else | ||||
| 	ld	r6, (VCPU_SHARED_MAS2)(r11) | ||||
| #endif | ||||
| 	lwz	r7, VCPU_SHARED_MAS7_3+4(r11) | ||||
| 	lwz	r8, VCPU_SHARED_MAS4(r11) | ||||
| 	mtspr	SPRN_MAS0, r3 | ||||
| 	mtspr	SPRN_MAS1, r5 | ||||
| 	mtspr	SPRN_MAS2, r6 | ||||
| 	mtspr	SPRN_MAS3, r7 | ||||
| 	mtspr	SPRN_MAS4, r8 | ||||
| 	lwz	r3, VCPU_SHARED_MAS6(r11) | ||||
| 	lwz	r5, VCPU_SHARED_MAS7_3+0(r11) | ||||
| 	mtspr	SPRN_MAS6, r3 | ||||
| 	mtspr	SPRN_MAS7, r5 | ||||
| 	/* Disable MAS register updates via exception */ | ||||
| 	mfspr	r3, SPRN_EPCR | ||||
| 	oris	r3, r3, SPRN_EPCR_DMIUH@h
 | ||||
| 	mtspr	SPRN_EPCR, r3 | ||||
| 
 | ||||
| 	/* | ||||
| 	 * Host interrupt handlers may have clobbered these guest-readable | ||||
| 	 * SPRGs, so we need to reload them here with the guest's values. | ||||
| 	 */ | ||||
| 	lwz	r3, VCPU_VRSAVE(r4) | ||||
| 	lwz	r5, VCPU_SHARED_SPRG4(r11) | ||||
| 	mtspr	SPRN_VRSAVE, r3 | ||||
| 	lwz	r6, VCPU_SHARED_SPRG5(r11) | ||||
| 	mtspr	SPRN_SPRG4W, r5 | ||||
| 	lwz	r7, VCPU_SHARED_SPRG6(r11) | ||||
| 	mtspr	SPRN_SPRG5W, r6 | ||||
| 	lwz	r8, VCPU_SHARED_SPRG7(r11) | ||||
| 	mtspr	SPRN_SPRG6W, r7 | ||||
| 	mtspr	SPRN_SPRG7W, r8 | ||||
| 
 | ||||
| 	/* Load some guest volatiles. */ | ||||
| 	PPC_LL	r3, VCPU_LR(r4) | ||||
| 	PPC_LL	r5, VCPU_XER(r4) | ||||
| 	PPC_LL	r6, VCPU_CTR(r4) | ||||
| 	PPC_LL	r7, VCPU_CR(r4) | ||||
| 	PPC_LL	r8, VCPU_PC(r4) | ||||
| #ifndef CONFIG_64BIT | ||||
| 	lwz	r9, (VCPU_SHARED_MSR + 4)(r11) | ||||
| #else | ||||
| 	ld	r9, (VCPU_SHARED_MSR)(r11) | ||||
| #endif | ||||
| 	PPC_LL	r0, VCPU_GPR(r0)(r4) | ||||
| 	PPC_LL	r1, VCPU_GPR(r1)(r4) | ||||
| 	PPC_LL	r2, VCPU_GPR(r2)(r4) | ||||
| 	PPC_LL	r10, VCPU_GPR(r10)(r4) | ||||
| 	PPC_LL	r11, VCPU_GPR(r11)(r4) | ||||
| 	PPC_LL	r12, VCPU_GPR(r12)(r4) | ||||
| 	PPC_LL	r13, VCPU_GPR(r13)(r4) | ||||
| 	mtlr	r3 | ||||
| 	mtxer	r5 | ||||
| 	mtctr	r6 | ||||
| 	mtcr	r7 | ||||
| 	mtsrr0	r8 | ||||
| 	mtsrr1	r9 | ||||
| 
 | ||||
| #ifdef CONFIG_KVM_EXIT_TIMING | ||||
| 	/* save enter time */ | ||||
| 1: | ||||
| 	mfspr	r6, SPRN_TBRU | ||||
| 	mfspr	r7, SPRN_TBRL | ||||
| 	mfspr	r8, SPRN_TBRU | ||||
| 	cmpw	r8, r6 | ||||
| 	PPC_STL	r7, VCPU_TIMING_LAST_ENTER_TBL(r4) | ||||
| 	bne	1b | ||||
| 	PPC_STL	r8, VCPU_TIMING_LAST_ENTER_TBU(r4) | ||||
| #endif | ||||
| 
 | ||||
| 	/* Finish loading guest volatiles and jump to guest. */ | ||||
| 	PPC_LL	r5, VCPU_GPR(r5)(r4) | ||||
| 	PPC_LL	r6, VCPU_GPR(r6)(r4) | ||||
| 	PPC_LL	r7, VCPU_GPR(r7)(r4) | ||||
| 	PPC_LL	r8, VCPU_GPR(r8)(r4) | ||||
| 	PPC_LL	r9, VCPU_GPR(r9)(r4) | ||||
| 
 | ||||
| 	PPC_LL	r3, VCPU_GPR(r3)(r4) | ||||
| 	PPC_LL	r4, VCPU_GPR(r4)(r4) | ||||
| 	rfi | ||||
|  | @ -114,6 +114,11 @@ int kvmppc_sanity_check(struct kvm_vcpu *vcpu) | |||
| 		goto out; | ||||
| #endif | ||||
| 
 | ||||
| #ifdef CONFIG_KVM_BOOKE_HV | ||||
| 	if (!cpu_has_feature(CPU_FTR_EMB_HV)) | ||||
| 		goto out; | ||||
| #endif | ||||
| 
 | ||||
| 	r = true; | ||||
| 
 | ||||
| out: | ||||
|  |  | |||
|  | @ -93,6 +93,12 @@ static inline void kvmppc_account_exit_stat(struct kvm_vcpu *vcpu, int type) | |||
| 	case SIGNAL_EXITS: | ||||
| 		vcpu->stat.signal_exits++; | ||||
| 		break; | ||||
| 	case DBELL_EXITS: | ||||
| 		vcpu->stat.dbell_exits++; | ||||
| 		break; | ||||
| 	case GDBELL_EXITS: | ||||
| 		vcpu->stat.gdbell_exits++; | ||||
| 		break; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Scott Wood
				Scott Wood