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_copy_type; | ||||
| 	u8 mmio_vsx_tx_sx_enabled; | ||||
| 	u8 mmio_vmx_copy_nums; | ||||
| 	u8 osi_needed; | ||||
| 	u8 osi_enabled; | ||||
| 	u8 papr_enabled; | ||||
|  | @ -804,6 +805,7 @@ struct kvm_vcpu_arch { | |||
| #define KVM_MMIO_REG_QPR	0x0040 | ||||
| #define KVM_MMIO_REG_FQPR	0x0060 | ||||
| #define KVM_MMIO_REG_VSX	0x0080 | ||||
| #define KVM_MMIO_REG_VMX	0x00c0 | ||||
| 
 | ||||
| #define __KVM_HAVE_ARCH_WQP | ||||
| #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, | ||||
| 				unsigned int rt, unsigned int bytes, | ||||
| 			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, | ||||
| 			       u64 val, unsigned int bytes, | ||||
| 			       int is_default_endian); | ||||
|  |  | |||
|  | @ -156,6 +156,12 @@ | |||
| #define OP_31_XOP_LFDX          599 | ||||
| #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_STFS 52 | ||||
| #define OP_STFSU 53 | ||||
|  |  | |||
|  | @ -58,6 +58,18 @@ static bool kvmppc_check_vsx_disabled(struct kvm_vcpu *vcpu) | |||
| } | ||||
| #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: | ||||
|  * 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_sp64_extend = 0; | ||||
| 	vcpu->arch.mmio_sign_extend = 0; | ||||
| 	vcpu->arch.mmio_vmx_copy_nums = 0; | ||||
| 
 | ||||
| 	switch (get_op(inst)) { | ||||
| 	case 31: | ||||
|  | @ -459,6 +472,29 @@ int kvmppc_emulate_loadstore(struct kvm_vcpu *vcpu) | |||
| 							 rs, 4, 1); | ||||
| 			break; | ||||
| #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: | ||||
| 			emulated = EMULATE_FAIL; | ||||
| 			break; | ||||
|  |  | |||
|  | @ -924,6 +924,34 @@ static inline void kvmppc_set_vsr_word(struct kvm_vcpu *vcpu, | |||
| } | ||||
| #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 | ||||
| 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_set_vsr_dword_dump(vcpu, gpr); | ||||
| 		break; | ||||
| #endif | ||||
| #ifdef CONFIG_ALTIVEC | ||||
| 	case KVM_MMIO_REG_VMX: | ||||
| 		kvmppc_set_vmx_dword(vcpu, gpr); | ||||
| 		break; | ||||
| #endif | ||||
| 	default: | ||||
| 		BUG(); | ||||
|  | @ -1302,6 +1335,111 @@ static int kvmppc_emulate_mmio_vsx_loadstore(struct kvm_vcpu *vcpu, | |||
| } | ||||
| #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 r = 0; | ||||
|  | @ -1420,6 +1558,18 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run) | |||
| 				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 | ||||
| 	} else if (vcpu->arch.osi_needed) { | ||||
| 		u64 *gprs = run->osi.gprs; | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Jose Ricardo Ziviani
						Jose Ricardo Ziviani