1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright (C) 2022 SiFive
6 * Vincent Chen <vincent.chen@sifive.com>
7 * Greentime Hu <greentime.hu@sifive.com>
10 #include <linux/errno.h>
11 #include <linux/err.h>
12 #include <linux/kvm_host.h>
13 #include <linux/uaccess.h>
14 #include <asm/cpufeature.h>
15 #include <asm/kvm_vcpu_vector.h>
16 #include <asm/vector.h>
18 #ifdef CONFIG_RISCV_ISA_V
19 void kvm_riscv_vcpu_vector_reset(struct kvm_vcpu *vcpu)
21 unsigned long *isa = vcpu->arch.isa;
22 struct kvm_cpu_context *cntx = &vcpu->arch.guest_context;
24 cntx->sstatus &= ~SR_VS;
25 if (riscv_isa_extension_available(isa, v)) {
26 cntx->sstatus |= SR_VS_INITIAL;
27 WARN_ON(!cntx->vector.datap);
28 memset(cntx->vector.datap, 0, riscv_v_vsize);
30 cntx->sstatus |= SR_VS_OFF;
34 static void kvm_riscv_vcpu_vector_clean(struct kvm_cpu_context *cntx)
36 cntx->sstatus &= ~SR_VS;
37 cntx->sstatus |= SR_VS_CLEAN;
40 void kvm_riscv_vcpu_guest_vector_save(struct kvm_cpu_context *cntx,
43 if ((cntx->sstatus & SR_VS) == SR_VS_DIRTY) {
44 if (riscv_isa_extension_available(isa, v))
45 __kvm_riscv_vector_save(cntx);
46 kvm_riscv_vcpu_vector_clean(cntx);
50 void kvm_riscv_vcpu_guest_vector_restore(struct kvm_cpu_context *cntx,
53 if ((cntx->sstatus & SR_VS) != SR_VS_OFF) {
54 if (riscv_isa_extension_available(isa, v))
55 __kvm_riscv_vector_restore(cntx);
56 kvm_riscv_vcpu_vector_clean(cntx);
60 void kvm_riscv_vcpu_host_vector_save(struct kvm_cpu_context *cntx)
62 /* No need to check host sstatus as it can be modified outside */
63 if (riscv_isa_extension_available(NULL, v))
64 __kvm_riscv_vector_save(cntx);
67 void kvm_riscv_vcpu_host_vector_restore(struct kvm_cpu_context *cntx)
69 if (riscv_isa_extension_available(NULL, v))
70 __kvm_riscv_vector_restore(cntx);
73 int kvm_riscv_vcpu_alloc_vector_context(struct kvm_vcpu *vcpu,
74 struct kvm_cpu_context *cntx)
76 cntx->vector.datap = kmalloc(riscv_v_vsize, GFP_KERNEL);
77 if (!cntx->vector.datap)
80 vcpu->arch.host_context.vector.datap = kzalloc(riscv_v_vsize, GFP_KERNEL);
81 if (!vcpu->arch.host_context.vector.datap)
87 void kvm_riscv_vcpu_free_vector_context(struct kvm_vcpu *vcpu)
89 kfree(vcpu->arch.guest_reset_context.vector.datap);
90 kfree(vcpu->arch.host_context.vector.datap);
94 static int kvm_riscv_vcpu_vreg_addr(struct kvm_vcpu *vcpu,
95 unsigned long reg_num,
99 struct kvm_cpu_context *cntx = &vcpu->arch.guest_context;
100 size_t vlenb = riscv_v_vsize / 32;
102 if (reg_num < KVM_REG_RISCV_VECTOR_REG(0)) {
103 if (reg_size != sizeof(unsigned long))
106 case KVM_REG_RISCV_VECTOR_CSR_REG(vstart):
107 *reg_addr = &cntx->vector.vstart;
109 case KVM_REG_RISCV_VECTOR_CSR_REG(vl):
110 *reg_addr = &cntx->vector.vl;
112 case KVM_REG_RISCV_VECTOR_CSR_REG(vtype):
113 *reg_addr = &cntx->vector.vtype;
115 case KVM_REG_RISCV_VECTOR_CSR_REG(vcsr):
116 *reg_addr = &cntx->vector.vcsr;
118 case KVM_REG_RISCV_VECTOR_CSR_REG(datap):
122 } else if (reg_num <= KVM_REG_RISCV_VECTOR_REG(31)) {
123 if (reg_size != vlenb)
125 *reg_addr = cntx->vector.datap +
126 (reg_num - KVM_REG_RISCV_VECTOR_REG(0)) * vlenb;
134 int kvm_riscv_vcpu_get_reg_vector(struct kvm_vcpu *vcpu,
135 const struct kvm_one_reg *reg)
137 unsigned long *isa = vcpu->arch.isa;
138 unsigned long __user *uaddr =
139 (unsigned long __user *)(unsigned long)reg->addr;
140 unsigned long reg_num = reg->id & ~(KVM_REG_ARCH_MASK |
142 KVM_REG_RISCV_VECTOR);
143 size_t reg_size = KVM_REG_SIZE(reg->id);
147 if (!riscv_isa_extension_available(isa, v))
150 rc = kvm_riscv_vcpu_vreg_addr(vcpu, reg_num, reg_size, ®_addr);
154 if (copy_to_user(uaddr, reg_addr, reg_size))
160 int kvm_riscv_vcpu_set_reg_vector(struct kvm_vcpu *vcpu,
161 const struct kvm_one_reg *reg)
163 unsigned long *isa = vcpu->arch.isa;
164 unsigned long __user *uaddr =
165 (unsigned long __user *)(unsigned long)reg->addr;
166 unsigned long reg_num = reg->id & ~(KVM_REG_ARCH_MASK |
168 KVM_REG_RISCV_VECTOR);
169 size_t reg_size = KVM_REG_SIZE(reg->id);
173 if (!riscv_isa_extension_available(isa, v))
176 rc = kvm_riscv_vcpu_vreg_addr(vcpu, reg_num, reg_size, ®_addr);
180 if (copy_from_user(reg_addr, uaddr, reg_size))