1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Copyright (C) 2022 SiFive
4 *
5 * Authors:
6 * Vincent Chen <vincent.chen@sifive.com>
7 * Greentime Hu <greentime.hu@sifive.com>
8 */
9
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>
17
18 #ifdef CONFIG_RISCV_ISA_V
kvm_riscv_vcpu_vector_reset(struct kvm_vcpu * vcpu)19 void kvm_riscv_vcpu_vector_reset(struct kvm_vcpu *vcpu)
20 {
21 unsigned long *isa = vcpu->arch.isa;
22 struct kvm_cpu_context *cntx = &vcpu->arch.guest_context;
23
24 cntx->sstatus &= ~SR_VS;
25
26 cntx->vector.vlenb = riscv_v_vsize / 32;
27
28 if (riscv_isa_extension_available(isa, v)) {
29 cntx->sstatus |= SR_VS_INITIAL;
30 WARN_ON(!cntx->vector.datap);
31 memset(cntx->vector.datap, 0, riscv_v_vsize);
32 } else {
33 cntx->sstatus |= SR_VS_OFF;
34 }
35 }
36
kvm_riscv_vcpu_vector_clean(struct kvm_cpu_context * cntx)37 static void kvm_riscv_vcpu_vector_clean(struct kvm_cpu_context *cntx)
38 {
39 cntx->sstatus &= ~SR_VS;
40 cntx->sstatus |= SR_VS_CLEAN;
41 }
42
kvm_riscv_vcpu_guest_vector_save(struct kvm_cpu_context * cntx,unsigned long * isa)43 void kvm_riscv_vcpu_guest_vector_save(struct kvm_cpu_context *cntx,
44 unsigned long *isa)
45 {
46 if ((cntx->sstatus & SR_VS) == SR_VS_DIRTY) {
47 if (riscv_isa_extension_available(isa, v))
48 __kvm_riscv_vector_save(cntx);
49 kvm_riscv_vcpu_vector_clean(cntx);
50 }
51 }
52
kvm_riscv_vcpu_guest_vector_restore(struct kvm_cpu_context * cntx,unsigned long * isa)53 void kvm_riscv_vcpu_guest_vector_restore(struct kvm_cpu_context *cntx,
54 unsigned long *isa)
55 {
56 if ((cntx->sstatus & SR_VS) != SR_VS_OFF) {
57 if (riscv_isa_extension_available(isa, v))
58 __kvm_riscv_vector_restore(cntx);
59 kvm_riscv_vcpu_vector_clean(cntx);
60 }
61 }
62
kvm_riscv_vcpu_host_vector_save(struct kvm_cpu_context * cntx)63 void kvm_riscv_vcpu_host_vector_save(struct kvm_cpu_context *cntx)
64 {
65 /* No need to check host sstatus as it can be modified outside */
66 if (riscv_isa_extension_available(NULL, v))
67 __kvm_riscv_vector_save(cntx);
68 }
69
kvm_riscv_vcpu_host_vector_restore(struct kvm_cpu_context * cntx)70 void kvm_riscv_vcpu_host_vector_restore(struct kvm_cpu_context *cntx)
71 {
72 if (riscv_isa_extension_available(NULL, v))
73 __kvm_riscv_vector_restore(cntx);
74 }
75
kvm_riscv_vcpu_alloc_vector_context(struct kvm_vcpu * vcpu)76 int kvm_riscv_vcpu_alloc_vector_context(struct kvm_vcpu *vcpu)
77 {
78 vcpu->arch.guest_context.vector.datap = kzalloc(riscv_v_vsize, GFP_KERNEL);
79 if (!vcpu->arch.guest_context.vector.datap)
80 return -ENOMEM;
81
82 vcpu->arch.host_context.vector.datap = kzalloc(riscv_v_vsize, GFP_KERNEL);
83 if (!vcpu->arch.host_context.vector.datap)
84 return -ENOMEM;
85
86 return 0;
87 }
88
kvm_riscv_vcpu_free_vector_context(struct kvm_vcpu * vcpu)89 void kvm_riscv_vcpu_free_vector_context(struct kvm_vcpu *vcpu)
90 {
91 kfree(vcpu->arch.guest_context.vector.datap);
92 kfree(vcpu->arch.host_context.vector.datap);
93 }
94 #endif
95
kvm_riscv_vcpu_vreg_addr(struct kvm_vcpu * vcpu,unsigned long reg_num,size_t reg_size,void ** reg_addr)96 static int kvm_riscv_vcpu_vreg_addr(struct kvm_vcpu *vcpu,
97 unsigned long reg_num,
98 size_t reg_size,
99 void **reg_addr)
100 {
101 struct kvm_cpu_context *cntx = &vcpu->arch.guest_context;
102 size_t vlenb = riscv_v_vsize / 32;
103
104 if (reg_num < KVM_REG_RISCV_VECTOR_REG(0)) {
105 if (reg_size != sizeof(unsigned long))
106 return -EINVAL;
107 switch (reg_num) {
108 case KVM_REG_RISCV_VECTOR_CSR_REG(vstart):
109 *reg_addr = &cntx->vector.vstart;
110 break;
111 case KVM_REG_RISCV_VECTOR_CSR_REG(vl):
112 *reg_addr = &cntx->vector.vl;
113 break;
114 case KVM_REG_RISCV_VECTOR_CSR_REG(vtype):
115 *reg_addr = &cntx->vector.vtype;
116 break;
117 case KVM_REG_RISCV_VECTOR_CSR_REG(vcsr):
118 *reg_addr = &cntx->vector.vcsr;
119 break;
120 case KVM_REG_RISCV_VECTOR_CSR_REG(vlenb):
121 *reg_addr = &cntx->vector.vlenb;
122 break;
123 case KVM_REG_RISCV_VECTOR_CSR_REG(datap):
124 default:
125 return -ENOENT;
126 }
127 } else if (reg_num <= KVM_REG_RISCV_VECTOR_REG(31)) {
128 if (reg_size != vlenb)
129 return -EINVAL;
130 *reg_addr = cntx->vector.datap +
131 (reg_num - KVM_REG_RISCV_VECTOR_REG(0)) * vlenb;
132 } else {
133 return -ENOENT;
134 }
135
136 return 0;
137 }
138
kvm_riscv_vcpu_get_reg_vector(struct kvm_vcpu * vcpu,const struct kvm_one_reg * reg)139 int kvm_riscv_vcpu_get_reg_vector(struct kvm_vcpu *vcpu,
140 const struct kvm_one_reg *reg)
141 {
142 unsigned long *isa = vcpu->arch.isa;
143 unsigned long __user *uaddr =
144 (unsigned long __user *)(unsigned long)reg->addr;
145 unsigned long reg_num = reg->id & ~(KVM_REG_ARCH_MASK |
146 KVM_REG_SIZE_MASK |
147 KVM_REG_RISCV_VECTOR);
148 size_t reg_size = KVM_REG_SIZE(reg->id);
149 void *reg_addr;
150 int rc;
151
152 if (!riscv_isa_extension_available(isa, v))
153 return -ENOENT;
154
155 rc = kvm_riscv_vcpu_vreg_addr(vcpu, reg_num, reg_size, ®_addr);
156 if (rc)
157 return rc;
158
159 if (copy_to_user(uaddr, reg_addr, reg_size))
160 return -EFAULT;
161
162 return 0;
163 }
164
kvm_riscv_vcpu_set_reg_vector(struct kvm_vcpu * vcpu,const struct kvm_one_reg * reg)165 int kvm_riscv_vcpu_set_reg_vector(struct kvm_vcpu *vcpu,
166 const struct kvm_one_reg *reg)
167 {
168 unsigned long *isa = vcpu->arch.isa;
169 unsigned long __user *uaddr =
170 (unsigned long __user *)(unsigned long)reg->addr;
171 unsigned long reg_num = reg->id & ~(KVM_REG_ARCH_MASK |
172 KVM_REG_SIZE_MASK |
173 KVM_REG_RISCV_VECTOR);
174 size_t reg_size = KVM_REG_SIZE(reg->id);
175 void *reg_addr;
176 int rc;
177
178 if (!riscv_isa_extension_available(isa, v))
179 return -ENOENT;
180
181 if (reg_num == KVM_REG_RISCV_VECTOR_CSR_REG(vlenb)) {
182 struct kvm_cpu_context *cntx = &vcpu->arch.guest_context;
183 unsigned long reg_val;
184
185 if (copy_from_user(®_val, uaddr, reg_size))
186 return -EFAULT;
187 if (reg_val != cntx->vector.vlenb)
188 return -EINVAL;
189
190 return 0;
191 }
192
193 rc = kvm_riscv_vcpu_vreg_addr(vcpu, reg_num, reg_size, ®_addr);
194 if (rc)
195 return rc;
196
197 if (copy_from_user(reg_addr, uaddr, reg_size))
198 return -EFAULT;
199
200 return 0;
201 }
202