xref: /linux/arch/riscv/kvm/vcpu_vector.c (revision 1b0975ee3bdd3eb19a47371c26fd7ef8f7f6b599)
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/hwcap.h>
15 #include <asm/kvm_vcpu_vector.h>
16 #include <asm/vector.h>
17 
18 #ifdef CONFIG_RISCV_ISA_V
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 	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);
29 	} else {
30 		cntx->sstatus |= SR_VS_OFF;
31 	}
32 }
33 
34 static void kvm_riscv_vcpu_vector_clean(struct kvm_cpu_context *cntx)
35 {
36 	cntx->sstatus &= ~SR_VS;
37 	cntx->sstatus |= SR_VS_CLEAN;
38 }
39 
40 void kvm_riscv_vcpu_guest_vector_save(struct kvm_cpu_context *cntx,
41 				      unsigned long *isa)
42 {
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);
47 	}
48 }
49 
50 void kvm_riscv_vcpu_guest_vector_restore(struct kvm_cpu_context *cntx,
51 					 unsigned long *isa)
52 {
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);
57 	}
58 }
59 
60 void kvm_riscv_vcpu_host_vector_save(struct kvm_cpu_context *cntx)
61 {
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);
65 }
66 
67 void kvm_riscv_vcpu_host_vector_restore(struct kvm_cpu_context *cntx)
68 {
69 	if (riscv_isa_extension_available(NULL, v))
70 		__kvm_riscv_vector_restore(cntx);
71 }
72 
73 int kvm_riscv_vcpu_alloc_vector_context(struct kvm_vcpu *vcpu,
74 					struct kvm_cpu_context *cntx)
75 {
76 	cntx->vector.datap = kmalloc(riscv_v_vsize, GFP_KERNEL);
77 	if (!cntx->vector.datap)
78 		return -ENOMEM;
79 
80 	vcpu->arch.host_context.vector.datap = kzalloc(riscv_v_vsize, GFP_KERNEL);
81 	if (!vcpu->arch.host_context.vector.datap)
82 		return -ENOMEM;
83 
84 	return 0;
85 }
86 
87 void kvm_riscv_vcpu_free_vector_context(struct kvm_vcpu *vcpu)
88 {
89 	kfree(vcpu->arch.guest_reset_context.vector.datap);
90 	kfree(vcpu->arch.host_context.vector.datap);
91 }
92 #endif
93 
94 static void *kvm_riscv_vcpu_vreg_addr(struct kvm_vcpu *vcpu,
95 				      unsigned long reg_num,
96 				      size_t reg_size)
97 {
98 	struct kvm_cpu_context *cntx = &vcpu->arch.guest_context;
99 	void *reg_val;
100 	size_t vlenb = riscv_v_vsize / 32;
101 
102 	if (reg_num < KVM_REG_RISCV_VECTOR_REG(0)) {
103 		if (reg_size != sizeof(unsigned long))
104 			return NULL;
105 		switch (reg_num) {
106 		case KVM_REG_RISCV_VECTOR_CSR_REG(vstart):
107 			reg_val = &cntx->vector.vstart;
108 			break;
109 		case KVM_REG_RISCV_VECTOR_CSR_REG(vl):
110 			reg_val = &cntx->vector.vl;
111 			break;
112 		case KVM_REG_RISCV_VECTOR_CSR_REG(vtype):
113 			reg_val = &cntx->vector.vtype;
114 			break;
115 		case KVM_REG_RISCV_VECTOR_CSR_REG(vcsr):
116 			reg_val = &cntx->vector.vcsr;
117 			break;
118 		case KVM_REG_RISCV_VECTOR_CSR_REG(datap):
119 		default:
120 			return NULL;
121 		}
122 	} else if (reg_num <= KVM_REG_RISCV_VECTOR_REG(31)) {
123 		if (reg_size != vlenb)
124 			return NULL;
125 		reg_val = cntx->vector.datap
126 			  + (reg_num - KVM_REG_RISCV_VECTOR_REG(0)) * vlenb;
127 	} else {
128 		return NULL;
129 	}
130 
131 	return reg_val;
132 }
133 
134 int kvm_riscv_vcpu_get_reg_vector(struct kvm_vcpu *vcpu,
135 				  const struct kvm_one_reg *reg,
136 				  unsigned long rtype)
137 {
138 	unsigned long *isa = vcpu->arch.isa;
139 	unsigned long __user *uaddr =
140 			(unsigned long __user *)(unsigned long)reg->addr;
141 	unsigned long reg_num = reg->id & ~(KVM_REG_ARCH_MASK |
142 					    KVM_REG_SIZE_MASK |
143 					    rtype);
144 	void *reg_val = NULL;
145 	size_t reg_size = KVM_REG_SIZE(reg->id);
146 
147 	if (rtype == KVM_REG_RISCV_VECTOR &&
148 	    riscv_isa_extension_available(isa, v)) {
149 		reg_val = kvm_riscv_vcpu_vreg_addr(vcpu, reg_num, reg_size);
150 	}
151 
152 	if (!reg_val)
153 		return -EINVAL;
154 
155 	if (copy_to_user(uaddr, reg_val, reg_size))
156 		return -EFAULT;
157 
158 	return 0;
159 }
160 
161 int kvm_riscv_vcpu_set_reg_vector(struct kvm_vcpu *vcpu,
162 				  const struct kvm_one_reg *reg,
163 				  unsigned long rtype)
164 {
165 	unsigned long *isa = vcpu->arch.isa;
166 	unsigned long __user *uaddr =
167 			(unsigned long __user *)(unsigned long)reg->addr;
168 	unsigned long reg_num = reg->id & ~(KVM_REG_ARCH_MASK |
169 					    KVM_REG_SIZE_MASK |
170 					    rtype);
171 	void *reg_val = NULL;
172 	size_t reg_size = KVM_REG_SIZE(reg->id);
173 
174 	if (rtype == KVM_REG_RISCV_VECTOR &&
175 	    riscv_isa_extension_available(isa, v)) {
176 		reg_val = kvm_riscv_vcpu_vreg_addr(vcpu, reg_num, reg_size);
177 	}
178 
179 	if (!reg_val)
180 		return -EINVAL;
181 
182 	if (copy_from_user(reg_val, uaddr, reg_size))
183 		return -EFAULT;
184 
185 	return 0;
186 }
187