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, ®_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(®_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, ®_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