forked from mirrors/linux
		
	KVM: PPC: Book3S: Add MMIO emulation for VMX instructions
This patch provides the MMIO load/store vector indexed X-Form emulation. Instructions implemented: lvx: the quadword in storage addressed by the result of EA & 0xffff_ffff_ffff_fff0 is loaded into VRT. stvx: the contents of VRS are stored into the quadword in storage addressed by the result of EA & 0xffff_ffff_ffff_fff0. Reported-by: Gopesh Kumar Chaudhary <gopchaud@in.ibm.com> Reported-by: Balamuruhan S <bala24@linux.vnet.ibm.com> Signed-off-by: Jose Ricardo Ziviani <joserz@linux.vnet.ibm.com> Signed-off-by: Paul Mackerras <paulus@ozlabs.org>
This commit is contained in:
		
							parent
							
								
									d20fe50a7b
								
							
						
					
					
						commit
						09f984961c
					
				
					 5 changed files with 198 additions and 0 deletions
				
			
		|  | @ -690,6 +690,7 @@ struct kvm_vcpu_arch { | ||||||
| 	u8 mmio_vsx_offset; | 	u8 mmio_vsx_offset; | ||||||
| 	u8 mmio_vsx_copy_type; | 	u8 mmio_vsx_copy_type; | ||||||
| 	u8 mmio_vsx_tx_sx_enabled; | 	u8 mmio_vsx_tx_sx_enabled; | ||||||
|  | 	u8 mmio_vmx_copy_nums; | ||||||
| 	u8 osi_needed; | 	u8 osi_needed; | ||||||
| 	u8 osi_enabled; | 	u8 osi_enabled; | ||||||
| 	u8 papr_enabled; | 	u8 papr_enabled; | ||||||
|  | @ -804,6 +805,7 @@ struct kvm_vcpu_arch { | ||||||
| #define KVM_MMIO_REG_QPR	0x0040 | #define KVM_MMIO_REG_QPR	0x0040 | ||||||
| #define KVM_MMIO_REG_FQPR	0x0060 | #define KVM_MMIO_REG_FQPR	0x0060 | ||||||
| #define KVM_MMIO_REG_VSX	0x0080 | #define KVM_MMIO_REG_VSX	0x0080 | ||||||
|  | #define KVM_MMIO_REG_VMX	0x00c0 | ||||||
| 
 | 
 | ||||||
| #define __KVM_HAVE_ARCH_WQP | #define __KVM_HAVE_ARCH_WQP | ||||||
| #define __KVM_HAVE_CREATE_DEVICE | #define __KVM_HAVE_CREATE_DEVICE | ||||||
|  |  | ||||||
|  | @ -81,6 +81,10 @@ extern int kvmppc_handle_loads(struct kvm_run *run, struct kvm_vcpu *vcpu, | ||||||
| extern int kvmppc_handle_vsx_load(struct kvm_run *run, struct kvm_vcpu *vcpu, | extern int kvmppc_handle_vsx_load(struct kvm_run *run, struct kvm_vcpu *vcpu, | ||||||
| 				unsigned int rt, unsigned int bytes, | 				unsigned int rt, unsigned int bytes, | ||||||
| 			int is_default_endian, int mmio_sign_extend); | 			int is_default_endian, int mmio_sign_extend); | ||||||
|  | extern int kvmppc_handle_load128_by2x64(struct kvm_run *run, | ||||||
|  | 		struct kvm_vcpu *vcpu, unsigned int rt, int is_default_endian); | ||||||
|  | extern int kvmppc_handle_store128_by2x64(struct kvm_run *run, | ||||||
|  | 		struct kvm_vcpu *vcpu, unsigned int rs, int is_default_endian); | ||||||
| extern int kvmppc_handle_store(struct kvm_run *run, struct kvm_vcpu *vcpu, | extern int kvmppc_handle_store(struct kvm_run *run, struct kvm_vcpu *vcpu, | ||||||
| 			       u64 val, unsigned int bytes, | 			       u64 val, unsigned int bytes, | ||||||
| 			       int is_default_endian); | 			       int is_default_endian); | ||||||
|  |  | ||||||
|  | @ -156,6 +156,12 @@ | ||||||
| #define OP_31_XOP_LFDX          599 | #define OP_31_XOP_LFDX          599 | ||||||
| #define OP_31_XOP_LFDUX		631 | #define OP_31_XOP_LFDUX		631 | ||||||
| 
 | 
 | ||||||
|  | /* VMX Vector Load Instructions */ | ||||||
|  | #define OP_31_XOP_LVX           103 | ||||||
|  | 
 | ||||||
|  | /* VMX Vector Store Instructions */ | ||||||
|  | #define OP_31_XOP_STVX          231 | ||||||
|  | 
 | ||||||
| #define OP_LWZ  32 | #define OP_LWZ  32 | ||||||
| #define OP_STFS 52 | #define OP_STFS 52 | ||||||
| #define OP_STFSU 53 | #define OP_STFSU 53 | ||||||
|  |  | ||||||
|  | @ -58,6 +58,18 @@ static bool kvmppc_check_vsx_disabled(struct kvm_vcpu *vcpu) | ||||||
| } | } | ||||||
| #endif /* CONFIG_VSX */ | #endif /* CONFIG_VSX */ | ||||||
| 
 | 
 | ||||||
|  | #ifdef CONFIG_ALTIVEC | ||||||
|  | static bool kvmppc_check_altivec_disabled(struct kvm_vcpu *vcpu) | ||||||
|  | { | ||||||
|  | 	if (!(kvmppc_get_msr(vcpu) & MSR_VEC)) { | ||||||
|  | 		kvmppc_core_queue_vec_unavail(vcpu); | ||||||
|  | 		return true; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return false; | ||||||
|  | } | ||||||
|  | #endif /* CONFIG_ALTIVEC */ | ||||||
|  | 
 | ||||||
| /*
 | /*
 | ||||||
|  * XXX to do: |  * XXX to do: | ||||||
|  * lfiwax, lfiwzx |  * lfiwax, lfiwzx | ||||||
|  | @ -98,6 +110,7 @@ int kvmppc_emulate_loadstore(struct kvm_vcpu *vcpu) | ||||||
| 	vcpu->arch.mmio_vsx_copy_type = KVMPPC_VSX_COPY_NONE; | 	vcpu->arch.mmio_vsx_copy_type = KVMPPC_VSX_COPY_NONE; | ||||||
| 	vcpu->arch.mmio_sp64_extend = 0; | 	vcpu->arch.mmio_sp64_extend = 0; | ||||||
| 	vcpu->arch.mmio_sign_extend = 0; | 	vcpu->arch.mmio_sign_extend = 0; | ||||||
|  | 	vcpu->arch.mmio_vmx_copy_nums = 0; | ||||||
| 
 | 
 | ||||||
| 	switch (get_op(inst)) { | 	switch (get_op(inst)) { | ||||||
| 	case 31: | 	case 31: | ||||||
|  | @ -459,6 +472,29 @@ int kvmppc_emulate_loadstore(struct kvm_vcpu *vcpu) | ||||||
| 							 rs, 4, 1); | 							 rs, 4, 1); | ||||||
| 			break; | 			break; | ||||||
| #endif /* CONFIG_VSX */ | #endif /* CONFIG_VSX */ | ||||||
|  | 
 | ||||||
|  | #ifdef CONFIG_ALTIVEC | ||||||
|  | 		case OP_31_XOP_LVX: | ||||||
|  | 			if (kvmppc_check_altivec_disabled(vcpu)) | ||||||
|  | 				return EMULATE_DONE; | ||||||
|  | 			vcpu->arch.vaddr_accessed &= ~0xFULL; | ||||||
|  | 			vcpu->arch.paddr_accessed &= ~0xFULL; | ||||||
|  | 			vcpu->arch.mmio_vmx_copy_nums = 2; | ||||||
|  | 			emulated = kvmppc_handle_load128_by2x64(run, vcpu, | ||||||
|  | 					KVM_MMIO_REG_VMX|rt, 1); | ||||||
|  | 			break; | ||||||
|  | 
 | ||||||
|  | 		case OP_31_XOP_STVX: | ||||||
|  | 			if (kvmppc_check_altivec_disabled(vcpu)) | ||||||
|  | 				return EMULATE_DONE; | ||||||
|  | 			vcpu->arch.vaddr_accessed &= ~0xFULL; | ||||||
|  | 			vcpu->arch.paddr_accessed &= ~0xFULL; | ||||||
|  | 			vcpu->arch.mmio_vmx_copy_nums = 2; | ||||||
|  | 			emulated = kvmppc_handle_store128_by2x64(run, vcpu, | ||||||
|  | 					rs, 1); | ||||||
|  | 			break; | ||||||
|  | #endif /* CONFIG_ALTIVEC */ | ||||||
|  | 
 | ||||||
| 		default: | 		default: | ||||||
| 			emulated = EMULATE_FAIL; | 			emulated = EMULATE_FAIL; | ||||||
| 			break; | 			break; | ||||||
|  |  | ||||||
|  | @ -924,6 +924,34 @@ static inline void kvmppc_set_vsr_word(struct kvm_vcpu *vcpu, | ||||||
| } | } | ||||||
| #endif /* CONFIG_VSX */ | #endif /* CONFIG_VSX */ | ||||||
| 
 | 
 | ||||||
|  | #ifdef CONFIG_ALTIVEC | ||||||
|  | static inline void kvmppc_set_vmx_dword(struct kvm_vcpu *vcpu, | ||||||
|  | 		u64 gpr) | ||||||
|  | { | ||||||
|  | 	int index = vcpu->arch.io_gpr & KVM_MMIO_REG_MASK; | ||||||
|  | 	u32 hi, lo; | ||||||
|  | 	u32 di; | ||||||
|  | 
 | ||||||
|  | #ifdef __BIG_ENDIAN | ||||||
|  | 	hi = gpr >> 32; | ||||||
|  | 	lo = gpr & 0xffffffff; | ||||||
|  | #else | ||||||
|  | 	lo = gpr >> 32; | ||||||
|  | 	hi = gpr & 0xffffffff; | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 	di = 2 - vcpu->arch.mmio_vmx_copy_nums;		/* doubleword index */ | ||||||
|  | 	if (di > 1) | ||||||
|  | 		return; | ||||||
|  | 
 | ||||||
|  | 	if (vcpu->arch.mmio_host_swabbed) | ||||||
|  | 		di = 1 - di; | ||||||
|  | 
 | ||||||
|  | 	VCPU_VSX_VR(vcpu, index).u[di * 2] = hi; | ||||||
|  | 	VCPU_VSX_VR(vcpu, index).u[di * 2 + 1] = lo; | ||||||
|  | } | ||||||
|  | #endif /* CONFIG_ALTIVEC */ | ||||||
|  | 
 | ||||||
| #ifdef CONFIG_PPC_FPU | #ifdef CONFIG_PPC_FPU | ||||||
| static inline u64 sp_to_dp(u32 fprs) | static inline u64 sp_to_dp(u32 fprs) | ||||||
| { | { | ||||||
|  | @ -1026,6 +1054,11 @@ static void kvmppc_complete_mmio_load(struct kvm_vcpu *vcpu, | ||||||
| 				KVMPPC_VSX_COPY_DWORD_LOAD_DUMP) | 				KVMPPC_VSX_COPY_DWORD_LOAD_DUMP) | ||||||
| 			kvmppc_set_vsr_dword_dump(vcpu, gpr); | 			kvmppc_set_vsr_dword_dump(vcpu, gpr); | ||||||
| 		break; | 		break; | ||||||
|  | #endif | ||||||
|  | #ifdef CONFIG_ALTIVEC | ||||||
|  | 	case KVM_MMIO_REG_VMX: | ||||||
|  | 		kvmppc_set_vmx_dword(vcpu, gpr); | ||||||
|  | 		break; | ||||||
| #endif | #endif | ||||||
| 	default: | 	default: | ||||||
| 		BUG(); | 		BUG(); | ||||||
|  | @ -1302,6 +1335,111 @@ static int kvmppc_emulate_mmio_vsx_loadstore(struct kvm_vcpu *vcpu, | ||||||
| } | } | ||||||
| #endif /* CONFIG_VSX */ | #endif /* CONFIG_VSX */ | ||||||
| 
 | 
 | ||||||
|  | #ifdef CONFIG_ALTIVEC | ||||||
|  | /* handle quadword load access in two halves */ | ||||||
|  | int kvmppc_handle_load128_by2x64(struct kvm_run *run, struct kvm_vcpu *vcpu, | ||||||
|  | 		unsigned int rt, int is_default_endian) | ||||||
|  | { | ||||||
|  | 	enum emulation_result emulated; | ||||||
|  | 
 | ||||||
|  | 	while (vcpu->arch.mmio_vmx_copy_nums) { | ||||||
|  | 		emulated = __kvmppc_handle_load(run, vcpu, rt, 8, | ||||||
|  | 				is_default_endian, 0); | ||||||
|  | 
 | ||||||
|  | 		if (emulated != EMULATE_DONE) | ||||||
|  | 			break; | ||||||
|  | 
 | ||||||
|  | 		vcpu->arch.paddr_accessed += run->mmio.len; | ||||||
|  | 		vcpu->arch.mmio_vmx_copy_nums--; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return emulated; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline int kvmppc_get_vmx_data(struct kvm_vcpu *vcpu, int rs, u64 *val) | ||||||
|  | { | ||||||
|  | 	vector128 vrs = VCPU_VSX_VR(vcpu, rs); | ||||||
|  | 	u32 di; | ||||||
|  | 	u64 w0, w1; | ||||||
|  | 
 | ||||||
|  | 	di = 2 - vcpu->arch.mmio_vmx_copy_nums;		/* doubleword index */ | ||||||
|  | 	if (di > 1) | ||||||
|  | 		return -1; | ||||||
|  | 
 | ||||||
|  | 	if (vcpu->arch.mmio_host_swabbed) | ||||||
|  | 		di = 1 - di; | ||||||
|  | 
 | ||||||
|  | 	w0 = vrs.u[di * 2]; | ||||||
|  | 	w1 = vrs.u[di * 2 + 1]; | ||||||
|  | 
 | ||||||
|  | #ifdef __BIG_ENDIAN | ||||||
|  | 	*val = (w0 << 32) | w1; | ||||||
|  | #else | ||||||
|  | 	*val = (w1 << 32) | w0; | ||||||
|  | #endif | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* handle quadword store in two halves */ | ||||||
|  | int kvmppc_handle_store128_by2x64(struct kvm_run *run, struct kvm_vcpu *vcpu, | ||||||
|  | 		unsigned int rs, int is_default_endian) | ||||||
|  | { | ||||||
|  | 	u64 val = 0; | ||||||
|  | 	enum emulation_result emulated = EMULATE_DONE; | ||||||
|  | 
 | ||||||
|  | 	vcpu->arch.io_gpr = rs; | ||||||
|  | 
 | ||||||
|  | 	while (vcpu->arch.mmio_vmx_copy_nums) { | ||||||
|  | 		if (kvmppc_get_vmx_data(vcpu, rs, &val) == -1) | ||||||
|  | 			return EMULATE_FAIL; | ||||||
|  | 
 | ||||||
|  | 		emulated = kvmppc_handle_store(run, vcpu, val, 8, | ||||||
|  | 				is_default_endian); | ||||||
|  | 		if (emulated != EMULATE_DONE) | ||||||
|  | 			break; | ||||||
|  | 
 | ||||||
|  | 		vcpu->arch.paddr_accessed += run->mmio.len; | ||||||
|  | 		vcpu->arch.mmio_vmx_copy_nums--; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return emulated; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int kvmppc_emulate_mmio_vmx_loadstore(struct kvm_vcpu *vcpu, | ||||||
|  | 		struct kvm_run *run) | ||||||
|  | { | ||||||
|  | 	enum emulation_result emulated = EMULATE_FAIL; | ||||||
|  | 	int r; | ||||||
|  | 
 | ||||||
|  | 	vcpu->arch.paddr_accessed += run->mmio.len; | ||||||
|  | 
 | ||||||
|  | 	if (!vcpu->mmio_is_write) { | ||||||
|  | 		emulated = kvmppc_handle_load128_by2x64(run, vcpu, | ||||||
|  | 				vcpu->arch.io_gpr, 1); | ||||||
|  | 	} else { | ||||||
|  | 		emulated = kvmppc_handle_store128_by2x64(run, vcpu, | ||||||
|  | 				vcpu->arch.io_gpr, 1); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	switch (emulated) { | ||||||
|  | 	case EMULATE_DO_MMIO: | ||||||
|  | 		run->exit_reason = KVM_EXIT_MMIO; | ||||||
|  | 		r = RESUME_HOST; | ||||||
|  | 		break; | ||||||
|  | 	case EMULATE_FAIL: | ||||||
|  | 		pr_info("KVM: MMIO emulation failed (VMX repeat)\n"); | ||||||
|  | 		run->exit_reason = KVM_EXIT_INTERNAL_ERROR; | ||||||
|  | 		run->internal.suberror = KVM_INTERNAL_ERROR_EMULATION; | ||||||
|  | 		r = RESUME_HOST; | ||||||
|  | 		break; | ||||||
|  | 	default: | ||||||
|  | 		r = RESUME_GUEST; | ||||||
|  | 		break; | ||||||
|  | 	} | ||||||
|  | 	return r; | ||||||
|  | } | ||||||
|  | #endif /* CONFIG_ALTIVEC */ | ||||||
|  | 
 | ||||||
| int kvm_vcpu_ioctl_get_one_reg(struct kvm_vcpu *vcpu, struct kvm_one_reg *reg) | int kvm_vcpu_ioctl_get_one_reg(struct kvm_vcpu *vcpu, struct kvm_one_reg *reg) | ||||||
| { | { | ||||||
| 	int r = 0; | 	int r = 0; | ||||||
|  | @ -1420,6 +1558,18 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run) | ||||||
| 				return r; | 				return r; | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  | #endif | ||||||
|  | #ifdef CONFIG_ALTIVEC | ||||||
|  | 		if (vcpu->arch.mmio_vmx_copy_nums > 0) | ||||||
|  | 			vcpu->arch.mmio_vmx_copy_nums--; | ||||||
|  | 
 | ||||||
|  | 		if (vcpu->arch.mmio_vmx_copy_nums > 0) { | ||||||
|  | 			r = kvmppc_emulate_mmio_vmx_loadstore(vcpu, run); | ||||||
|  | 			if (r == RESUME_HOST) { | ||||||
|  | 				vcpu->mmio_needed = 1; | ||||||
|  | 				return r; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
| #endif | #endif | ||||||
| 	} else if (vcpu->arch.osi_needed) { | 	} else if (vcpu->arch.osi_needed) { | ||||||
| 		u64 *gprs = run->osi.gprs; | 		u64 *gprs = run->osi.gprs; | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Jose Ricardo Ziviani
						Jose Ricardo Ziviani