xref: /linux/arch/riscv/kvm/vcpu_vector.c (revision 06d07429858317ded2db7986113a9e0129cd599b)
10f4b8257SVincent Chen // SPDX-License-Identifier: GPL-2.0
20f4b8257SVincent Chen /*
30f4b8257SVincent Chen  * Copyright (C) 2022 SiFive
40f4b8257SVincent Chen  *
50f4b8257SVincent Chen  * Authors:
60f4b8257SVincent Chen  *     Vincent Chen <vincent.chen@sifive.com>
70f4b8257SVincent Chen  *     Greentime Hu <greentime.hu@sifive.com>
80f4b8257SVincent Chen  */
90f4b8257SVincent Chen 
100f4b8257SVincent Chen #include <linux/errno.h>
110f4b8257SVincent Chen #include <linux/err.h>
120f4b8257SVincent Chen #include <linux/kvm_host.h>
130f4b8257SVincent Chen #include <linux/uaccess.h>
14e72c4333SXiao Wang #include <asm/cpufeature.h>
150f4b8257SVincent Chen #include <asm/kvm_vcpu_vector.h>
160f4b8257SVincent Chen #include <asm/vector.h>
170f4b8257SVincent Chen 
180f4b8257SVincent Chen #ifdef CONFIG_RISCV_ISA_V
kvm_riscv_vcpu_vector_reset(struct kvm_vcpu * vcpu)190f4b8257SVincent Chen void kvm_riscv_vcpu_vector_reset(struct kvm_vcpu *vcpu)
200f4b8257SVincent Chen {
210f4b8257SVincent Chen 	unsigned long *isa = vcpu->arch.isa;
220f4b8257SVincent Chen 	struct kvm_cpu_context *cntx = &vcpu->arch.guest_context;
230f4b8257SVincent Chen 
240f4b8257SVincent Chen 	cntx->sstatus &= ~SR_VS;
250f4b8257SVincent Chen 	if (riscv_isa_extension_available(isa, v)) {
260f4b8257SVincent Chen 		cntx->sstatus |= SR_VS_INITIAL;
270f4b8257SVincent Chen 		WARN_ON(!cntx->vector.datap);
280f4b8257SVincent Chen 		memset(cntx->vector.datap, 0, riscv_v_vsize);
290f4b8257SVincent Chen 	} else {
300f4b8257SVincent Chen 		cntx->sstatus |= SR_VS_OFF;
310f4b8257SVincent Chen 	}
320f4b8257SVincent Chen }
330f4b8257SVincent Chen 
kvm_riscv_vcpu_vector_clean(struct kvm_cpu_context * cntx)340f4b8257SVincent Chen static void kvm_riscv_vcpu_vector_clean(struct kvm_cpu_context *cntx)
350f4b8257SVincent Chen {
360f4b8257SVincent Chen 	cntx->sstatus &= ~SR_VS;
370f4b8257SVincent Chen 	cntx->sstatus |= SR_VS_CLEAN;
380f4b8257SVincent Chen }
390f4b8257SVincent Chen 
kvm_riscv_vcpu_guest_vector_save(struct kvm_cpu_context * cntx,unsigned long * isa)400f4b8257SVincent Chen void kvm_riscv_vcpu_guest_vector_save(struct kvm_cpu_context *cntx,
410f4b8257SVincent Chen 				      unsigned long *isa)
420f4b8257SVincent Chen {
430f4b8257SVincent Chen 	if ((cntx->sstatus & SR_VS) == SR_VS_DIRTY) {
440f4b8257SVincent Chen 		if (riscv_isa_extension_available(isa, v))
450f4b8257SVincent Chen 			__kvm_riscv_vector_save(cntx);
460f4b8257SVincent Chen 		kvm_riscv_vcpu_vector_clean(cntx);
470f4b8257SVincent Chen 	}
480f4b8257SVincent Chen }
490f4b8257SVincent Chen 
kvm_riscv_vcpu_guest_vector_restore(struct kvm_cpu_context * cntx,unsigned long * isa)500f4b8257SVincent Chen void kvm_riscv_vcpu_guest_vector_restore(struct kvm_cpu_context *cntx,
510f4b8257SVincent Chen 					 unsigned long *isa)
520f4b8257SVincent Chen {
530f4b8257SVincent Chen 	if ((cntx->sstatus & SR_VS) != SR_VS_OFF) {
540f4b8257SVincent Chen 		if (riscv_isa_extension_available(isa, v))
550f4b8257SVincent Chen 			__kvm_riscv_vector_restore(cntx);
560f4b8257SVincent Chen 		kvm_riscv_vcpu_vector_clean(cntx);
570f4b8257SVincent Chen 	}
580f4b8257SVincent Chen }
590f4b8257SVincent Chen 
kvm_riscv_vcpu_host_vector_save(struct kvm_cpu_context * cntx)600f4b8257SVincent Chen void kvm_riscv_vcpu_host_vector_save(struct kvm_cpu_context *cntx)
610f4b8257SVincent Chen {
620f4b8257SVincent Chen 	/* No need to check host sstatus as it can be modified outside */
630f4b8257SVincent Chen 	if (riscv_isa_extension_available(NULL, v))
640f4b8257SVincent Chen 		__kvm_riscv_vector_save(cntx);
650f4b8257SVincent Chen }
660f4b8257SVincent Chen 
kvm_riscv_vcpu_host_vector_restore(struct kvm_cpu_context * cntx)670f4b8257SVincent Chen void kvm_riscv_vcpu_host_vector_restore(struct kvm_cpu_context *cntx)
680f4b8257SVincent Chen {
690f4b8257SVincent Chen 	if (riscv_isa_extension_available(NULL, v))
700f4b8257SVincent Chen 		__kvm_riscv_vector_restore(cntx);
710f4b8257SVincent Chen }
720f4b8257SVincent Chen 
kvm_riscv_vcpu_alloc_vector_context(struct kvm_vcpu * vcpu,struct kvm_cpu_context * cntx)730f4b8257SVincent Chen int kvm_riscv_vcpu_alloc_vector_context(struct kvm_vcpu *vcpu,
740f4b8257SVincent Chen 					struct kvm_cpu_context *cntx)
750f4b8257SVincent Chen {
760f4b8257SVincent Chen 	cntx->vector.datap = kmalloc(riscv_v_vsize, GFP_KERNEL);
770f4b8257SVincent Chen 	if (!cntx->vector.datap)
780f4b8257SVincent Chen 		return -ENOMEM;
79197bd237SDaniel Henrique Barboza 	cntx->vector.vlenb = riscv_v_vsize / 32;
800f4b8257SVincent Chen 
810f4b8257SVincent Chen 	vcpu->arch.host_context.vector.datap = kzalloc(riscv_v_vsize, GFP_KERNEL);
820f4b8257SVincent Chen 	if (!vcpu->arch.host_context.vector.datap)
830f4b8257SVincent Chen 		return -ENOMEM;
840f4b8257SVincent Chen 
850f4b8257SVincent Chen 	return 0;
860f4b8257SVincent Chen }
870f4b8257SVincent Chen 
kvm_riscv_vcpu_free_vector_context(struct kvm_vcpu * vcpu)880f4b8257SVincent Chen void kvm_riscv_vcpu_free_vector_context(struct kvm_vcpu *vcpu)
890f4b8257SVincent Chen {
900f4b8257SVincent Chen 	kfree(vcpu->arch.guest_reset_context.vector.datap);
910f4b8257SVincent Chen 	kfree(vcpu->arch.host_context.vector.datap);
920f4b8257SVincent Chen }
930f4b8257SVincent Chen #endif
940f4b8257SVincent Chen 
kvm_riscv_vcpu_vreg_addr(struct kvm_vcpu * vcpu,unsigned long reg_num,size_t reg_size,void ** reg_addr)951deaf754SAndrew Jones static int kvm_riscv_vcpu_vreg_addr(struct kvm_vcpu *vcpu,
960f4b8257SVincent Chen 				    unsigned long reg_num,
971deaf754SAndrew Jones 				    size_t reg_size,
98630b4ceeSAndrew Jones 				    void **reg_addr)
990f4b8257SVincent Chen {
1000f4b8257SVincent Chen 	struct kvm_cpu_context *cntx = &vcpu->arch.guest_context;
1010f4b8257SVincent Chen 	size_t vlenb = riscv_v_vsize / 32;
1020f4b8257SVincent Chen 
1030f4b8257SVincent Chen 	if (reg_num < KVM_REG_RISCV_VECTOR_REG(0)) {
1040f4b8257SVincent Chen 		if (reg_size != sizeof(unsigned long))
1051deaf754SAndrew Jones 			return -EINVAL;
1060f4b8257SVincent Chen 		switch (reg_num) {
1070f4b8257SVincent Chen 		case KVM_REG_RISCV_VECTOR_CSR_REG(vstart):
108630b4ceeSAndrew Jones 			*reg_addr = &cntx->vector.vstart;
1090f4b8257SVincent Chen 			break;
1100f4b8257SVincent Chen 		case KVM_REG_RISCV_VECTOR_CSR_REG(vl):
111630b4ceeSAndrew Jones 			*reg_addr = &cntx->vector.vl;
1120f4b8257SVincent Chen 			break;
1130f4b8257SVincent Chen 		case KVM_REG_RISCV_VECTOR_CSR_REG(vtype):
114630b4ceeSAndrew Jones 			*reg_addr = &cntx->vector.vtype;
1150f4b8257SVincent Chen 			break;
1160f4b8257SVincent Chen 		case KVM_REG_RISCV_VECTOR_CSR_REG(vcsr):
117630b4ceeSAndrew Jones 			*reg_addr = &cntx->vector.vcsr;
1180f4b8257SVincent Chen 			break;
119*2fa29037SDaniel Henrique Barboza 		case KVM_REG_RISCV_VECTOR_CSR_REG(vlenb):
120*2fa29037SDaniel Henrique Barboza 			*reg_addr = &cntx->vector.vlenb;
121*2fa29037SDaniel Henrique Barboza 			break;
1220f4b8257SVincent Chen 		case KVM_REG_RISCV_VECTOR_CSR_REG(datap):
1230f4b8257SVincent Chen 		default:
1241deaf754SAndrew Jones 			return -ENOENT;
1250f4b8257SVincent Chen 		}
1260f4b8257SVincent Chen 	} else if (reg_num <= KVM_REG_RISCV_VECTOR_REG(31)) {
1270f4b8257SVincent Chen 		if (reg_size != vlenb)
1281deaf754SAndrew Jones 			return -EINVAL;
129630b4ceeSAndrew Jones 		*reg_addr = cntx->vector.datap +
130630b4ceeSAndrew Jones 			    (reg_num - KVM_REG_RISCV_VECTOR_REG(0)) * vlenb;
1310f4b8257SVincent Chen 	} else {
1321deaf754SAndrew Jones 		return -ENOENT;
1330f4b8257SVincent Chen 	}
1340f4b8257SVincent Chen 
1351deaf754SAndrew Jones 	return 0;
1360f4b8257SVincent Chen }
1370f4b8257SVincent Chen 
kvm_riscv_vcpu_get_reg_vector(struct kvm_vcpu * vcpu,const struct kvm_one_reg * reg)1380f4b8257SVincent Chen int kvm_riscv_vcpu_get_reg_vector(struct kvm_vcpu *vcpu,
139630b4ceeSAndrew Jones 				  const struct kvm_one_reg *reg)
1400f4b8257SVincent Chen {
1410f4b8257SVincent Chen 	unsigned long *isa = vcpu->arch.isa;
1420f4b8257SVincent Chen 	unsigned long __user *uaddr =
1430f4b8257SVincent Chen 			(unsigned long __user *)(unsigned long)reg->addr;
1440f4b8257SVincent Chen 	unsigned long reg_num = reg->id & ~(KVM_REG_ARCH_MASK |
1450f4b8257SVincent Chen 					    KVM_REG_SIZE_MASK |
146630b4ceeSAndrew Jones 					    KVM_REG_RISCV_VECTOR);
1470f4b8257SVincent Chen 	size_t reg_size = KVM_REG_SIZE(reg->id);
148630b4ceeSAndrew Jones 	void *reg_addr;
1491deaf754SAndrew Jones 	int rc;
1500f4b8257SVincent Chen 
1511deaf754SAndrew Jones 	if (!riscv_isa_extension_available(isa, v))
1521deaf754SAndrew Jones 		return -ENOENT;
1531deaf754SAndrew Jones 
154630b4ceeSAndrew Jones 	rc = kvm_riscv_vcpu_vreg_addr(vcpu, reg_num, reg_size, &reg_addr);
1551deaf754SAndrew Jones 	if (rc)
1561deaf754SAndrew Jones 		return rc;
1571deaf754SAndrew Jones 
158630b4ceeSAndrew Jones 	if (copy_to_user(uaddr, reg_addr, reg_size))
1590f4b8257SVincent Chen 		return -EFAULT;
1600f4b8257SVincent Chen 
1610f4b8257SVincent Chen 	return 0;
1620f4b8257SVincent Chen }
1630f4b8257SVincent Chen 
kvm_riscv_vcpu_set_reg_vector(struct kvm_vcpu * vcpu,const struct kvm_one_reg * reg)1640f4b8257SVincent Chen int kvm_riscv_vcpu_set_reg_vector(struct kvm_vcpu *vcpu,
165630b4ceeSAndrew Jones 				  const struct kvm_one_reg *reg)
1660f4b8257SVincent Chen {
1670f4b8257SVincent Chen 	unsigned long *isa = vcpu->arch.isa;
1680f4b8257SVincent Chen 	unsigned long __user *uaddr =
1690f4b8257SVincent Chen 			(unsigned long __user *)(unsigned long)reg->addr;
1700f4b8257SVincent Chen 	unsigned long reg_num = reg->id & ~(KVM_REG_ARCH_MASK |
1710f4b8257SVincent Chen 					    KVM_REG_SIZE_MASK |
172630b4ceeSAndrew Jones 					    KVM_REG_RISCV_VECTOR);
1730f4b8257SVincent Chen 	size_t reg_size = KVM_REG_SIZE(reg->id);
174630b4ceeSAndrew Jones 	void *reg_addr;
1751deaf754SAndrew Jones 	int rc;
1760f4b8257SVincent Chen 
1771deaf754SAndrew Jones 	if (!riscv_isa_extension_available(isa, v))
1781deaf754SAndrew Jones 		return -ENOENT;
1791deaf754SAndrew Jones 
180*2fa29037SDaniel Henrique Barboza 	if (reg_num == KVM_REG_RISCV_VECTOR_CSR_REG(vlenb)) {
181*2fa29037SDaniel Henrique Barboza 		struct kvm_cpu_context *cntx = &vcpu->arch.guest_context;
182*2fa29037SDaniel Henrique Barboza 		unsigned long reg_val;
183*2fa29037SDaniel Henrique Barboza 
184*2fa29037SDaniel Henrique Barboza 		if (copy_from_user(&reg_val, uaddr, reg_size))
185*2fa29037SDaniel Henrique Barboza 			return -EFAULT;
186*2fa29037SDaniel Henrique Barboza 		if (reg_val != cntx->vector.vlenb)
187*2fa29037SDaniel Henrique Barboza 			return -EINVAL;
188*2fa29037SDaniel Henrique Barboza 
189*2fa29037SDaniel Henrique Barboza 		return 0;
190*2fa29037SDaniel Henrique Barboza 	}
191*2fa29037SDaniel Henrique Barboza 
192630b4ceeSAndrew Jones 	rc = kvm_riscv_vcpu_vreg_addr(vcpu, reg_num, reg_size, &reg_addr);
1931deaf754SAndrew Jones 	if (rc)
1941deaf754SAndrew Jones 		return rc;
1951deaf754SAndrew Jones 
196630b4ceeSAndrew Jones 	if (copy_from_user(reg_addr, uaddr, reg_size))
1970f4b8257SVincent Chen 		return -EFAULT;
1980f4b8257SVincent Chen 
1990f4b8257SVincent Chen 	return 0;
2000f4b8257SVincent Chen }
201