xref: /linux/arch/riscv/kvm/vcpu_sbi_fwft.c (revision e3966940559d52aa1800a008dcfeec218dd31f88)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (c) 2025 Rivos Inc.
4  *
5  * Authors:
6  *     Clément Léger <cleger@rivosinc.com>
7  */
8 
9 #include <linux/errno.h>
10 #include <linux/err.h>
11 #include <linux/kvm_host.h>
12 #include <asm/cpufeature.h>
13 #include <asm/sbi.h>
14 #include <asm/kvm_vcpu_sbi.h>
15 #include <asm/kvm_vcpu_sbi_fwft.h>
16 
17 #define MIS_DELEG (BIT_ULL(EXC_LOAD_MISALIGNED) | BIT_ULL(EXC_STORE_MISALIGNED))
18 
19 struct kvm_sbi_fwft_feature {
20 	/**
21 	 * @id: Feature ID
22 	 */
23 	enum sbi_fwft_feature_t id;
24 
25 	/**
26 	 * @first_reg_num: ONE_REG index of the first ONE_REG register
27 	 */
28 	unsigned long first_reg_num;
29 
30 	/**
31 	 * @supported: Check if the feature is supported on the vcpu
32 	 *
33 	 * This callback is optional, if not provided the feature is assumed to
34 	 * be supported
35 	 */
36 	bool (*supported)(struct kvm_vcpu *vcpu);
37 
38 	/**
39 	 * @reset: Reset the feature value irrespective whether feature is supported or not
40 	 *
41 	 * This callback is mandatory
42 	 */
43 	void (*reset)(struct kvm_vcpu *vcpu);
44 
45 	/**
46 	 * @set: Set the feature value
47 	 *
48 	 * Return SBI_SUCCESS on success or an SBI error (SBI_ERR_*)
49 	 *
50 	 * This callback is mandatory
51 	 */
52 	long (*set)(struct kvm_vcpu *vcpu, struct kvm_sbi_fwft_config *conf,
53 		    bool one_reg_access, unsigned long value);
54 
55 	/**
56 	 * @get: Get the feature current value
57 	 *
58 	 * Return SBI_SUCCESS on success or an SBI error (SBI_ERR_*)
59 	 *
60 	 * This callback is mandatory
61 	 */
62 	long (*get)(struct kvm_vcpu *vcpu, struct kvm_sbi_fwft_config *conf,
63 		    bool one_reg_access, unsigned long *value);
64 };
65 
66 static const enum sbi_fwft_feature_t kvm_fwft_defined_features[] = {
67 	SBI_FWFT_MISALIGNED_EXC_DELEG,
68 	SBI_FWFT_LANDING_PAD,
69 	SBI_FWFT_SHADOW_STACK,
70 	SBI_FWFT_DOUBLE_TRAP,
71 	SBI_FWFT_PTE_AD_HW_UPDATING,
72 	SBI_FWFT_POINTER_MASKING_PMLEN,
73 };
74 
75 static bool kvm_fwft_is_defined_feature(enum sbi_fwft_feature_t feature)
76 {
77 	int i;
78 
79 	for (i = 0; i < ARRAY_SIZE(kvm_fwft_defined_features); i++) {
80 		if (kvm_fwft_defined_features[i] == feature)
81 			return true;
82 	}
83 
84 	return false;
85 }
86 
87 static bool kvm_sbi_fwft_misaligned_delegation_supported(struct kvm_vcpu *vcpu)
88 {
89 	return misaligned_traps_can_delegate();
90 }
91 
92 static void kvm_sbi_fwft_reset_misaligned_delegation(struct kvm_vcpu *vcpu)
93 {
94 	struct kvm_vcpu_config *cfg = &vcpu->arch.cfg;
95 
96 	cfg->hedeleg &= ~MIS_DELEG;
97 }
98 
99 static long kvm_sbi_fwft_set_misaligned_delegation(struct kvm_vcpu *vcpu,
100 					struct kvm_sbi_fwft_config *conf,
101 					bool one_reg_access, unsigned long value)
102 {
103 	struct kvm_vcpu_config *cfg = &vcpu->arch.cfg;
104 
105 	if (value == 1) {
106 		cfg->hedeleg |= MIS_DELEG;
107 		if (!one_reg_access)
108 			csr_set(CSR_HEDELEG, MIS_DELEG);
109 	} else if (value == 0) {
110 		cfg->hedeleg &= ~MIS_DELEG;
111 		if (!one_reg_access)
112 			csr_clear(CSR_HEDELEG, MIS_DELEG);
113 	} else {
114 		return SBI_ERR_INVALID_PARAM;
115 	}
116 
117 	return SBI_SUCCESS;
118 }
119 
120 static long kvm_sbi_fwft_get_misaligned_delegation(struct kvm_vcpu *vcpu,
121 					struct kvm_sbi_fwft_config *conf,
122 					bool one_reg_access, unsigned long *value)
123 {
124 	struct kvm_vcpu_config *cfg = &vcpu->arch.cfg;
125 
126 	*value = (cfg->hedeleg & MIS_DELEG) == MIS_DELEG;
127 	return SBI_SUCCESS;
128 }
129 
130 #ifndef CONFIG_32BIT
131 
132 static bool try_to_set_pmm(unsigned long value)
133 {
134 	csr_set(CSR_HENVCFG, value);
135 	return (csr_read_clear(CSR_HENVCFG, ENVCFG_PMM) & ENVCFG_PMM) == value;
136 }
137 
138 static bool kvm_sbi_fwft_pointer_masking_pmlen_supported(struct kvm_vcpu *vcpu)
139 {
140 	struct kvm_sbi_fwft *fwft = vcpu_to_fwft(vcpu);
141 
142 	if (!riscv_isa_extension_available(vcpu->arch.isa, SMNPM))
143 		return false;
144 
145 	fwft->have_vs_pmlen_7 = try_to_set_pmm(ENVCFG_PMM_PMLEN_7);
146 	fwft->have_vs_pmlen_16 = try_to_set_pmm(ENVCFG_PMM_PMLEN_16);
147 
148 	return fwft->have_vs_pmlen_7 || fwft->have_vs_pmlen_16;
149 }
150 
151 static void kvm_sbi_fwft_reset_pointer_masking_pmlen(struct kvm_vcpu *vcpu)
152 {
153 	vcpu->arch.cfg.henvcfg &= ~ENVCFG_PMM;
154 }
155 
156 static long kvm_sbi_fwft_set_pointer_masking_pmlen(struct kvm_vcpu *vcpu,
157 						   struct kvm_sbi_fwft_config *conf,
158 						   bool one_reg_access, unsigned long value)
159 {
160 	struct kvm_sbi_fwft *fwft = vcpu_to_fwft(vcpu);
161 	unsigned long pmm;
162 
163 	switch (value) {
164 	case 0:
165 		pmm = ENVCFG_PMM_PMLEN_0;
166 		break;
167 	case 7:
168 		if (!fwft->have_vs_pmlen_7)
169 			return SBI_ERR_INVALID_PARAM;
170 		pmm = ENVCFG_PMM_PMLEN_7;
171 		break;
172 	case 16:
173 		if (!fwft->have_vs_pmlen_16)
174 			return SBI_ERR_INVALID_PARAM;
175 		pmm = ENVCFG_PMM_PMLEN_16;
176 		break;
177 	default:
178 		return SBI_ERR_INVALID_PARAM;
179 	}
180 
181 	vcpu->arch.cfg.henvcfg &= ~ENVCFG_PMM;
182 	vcpu->arch.cfg.henvcfg |= pmm;
183 
184 	/*
185 	 * Instead of waiting for vcpu_load/put() to update HENVCFG CSR,
186 	 * update here so that VCPU see's pointer masking mode change
187 	 * immediately.
188 	 */
189 	if (!one_reg_access)
190 		csr_write(CSR_HENVCFG, vcpu->arch.cfg.henvcfg);
191 
192 	return SBI_SUCCESS;
193 }
194 
195 static long kvm_sbi_fwft_get_pointer_masking_pmlen(struct kvm_vcpu *vcpu,
196 						   struct kvm_sbi_fwft_config *conf,
197 						   bool one_reg_access, unsigned long *value)
198 {
199 	switch (vcpu->arch.cfg.henvcfg & ENVCFG_PMM) {
200 	case ENVCFG_PMM_PMLEN_0:
201 		*value = 0;
202 		break;
203 	case ENVCFG_PMM_PMLEN_7:
204 		*value = 7;
205 		break;
206 	case ENVCFG_PMM_PMLEN_16:
207 		*value = 16;
208 		break;
209 	default:
210 		return SBI_ERR_FAILURE;
211 	}
212 
213 	return SBI_SUCCESS;
214 }
215 
216 #endif
217 
218 static const struct kvm_sbi_fwft_feature features[] = {
219 	{
220 		.id = SBI_FWFT_MISALIGNED_EXC_DELEG,
221 		.first_reg_num = offsetof(struct kvm_riscv_sbi_fwft, misaligned_deleg.enable) /
222 				 sizeof(unsigned long),
223 		.supported = kvm_sbi_fwft_misaligned_delegation_supported,
224 		.reset = kvm_sbi_fwft_reset_misaligned_delegation,
225 		.set = kvm_sbi_fwft_set_misaligned_delegation,
226 		.get = kvm_sbi_fwft_get_misaligned_delegation,
227 	},
228 #ifndef CONFIG_32BIT
229 	{
230 		.id = SBI_FWFT_POINTER_MASKING_PMLEN,
231 		.first_reg_num = offsetof(struct kvm_riscv_sbi_fwft, pointer_masking.enable) /
232 				 sizeof(unsigned long),
233 		.supported = kvm_sbi_fwft_pointer_masking_pmlen_supported,
234 		.reset = kvm_sbi_fwft_reset_pointer_masking_pmlen,
235 		.set = kvm_sbi_fwft_set_pointer_masking_pmlen,
236 		.get = kvm_sbi_fwft_get_pointer_masking_pmlen,
237 	},
238 #endif
239 };
240 
241 static const struct kvm_sbi_fwft_feature *kvm_sbi_fwft_regnum_to_feature(unsigned long reg_num)
242 {
243 	const struct kvm_sbi_fwft_feature *feature;
244 	int i;
245 
246 	for (i = 0; i < ARRAY_SIZE(features); i++) {
247 		feature = &features[i];
248 		if (feature->first_reg_num <= reg_num && reg_num < (feature->first_reg_num + 3))
249 			return feature;
250 	}
251 
252 	return NULL;
253 }
254 
255 static struct kvm_sbi_fwft_config *
256 kvm_sbi_fwft_get_config(struct kvm_vcpu *vcpu, enum sbi_fwft_feature_t feature)
257 {
258 	int i;
259 	struct kvm_sbi_fwft *fwft = vcpu_to_fwft(vcpu);
260 
261 	for (i = 0; i < ARRAY_SIZE(features); i++) {
262 		if (fwft->configs[i].feature->id == feature)
263 			return &fwft->configs[i];
264 	}
265 
266 	return NULL;
267 }
268 
269 static int kvm_fwft_get_feature(struct kvm_vcpu *vcpu, u32 feature,
270 				struct kvm_sbi_fwft_config **conf)
271 {
272 	struct kvm_sbi_fwft_config *tconf;
273 
274 	tconf = kvm_sbi_fwft_get_config(vcpu, feature);
275 	if (!tconf) {
276 		if (kvm_fwft_is_defined_feature(feature))
277 			return SBI_ERR_NOT_SUPPORTED;
278 
279 		return SBI_ERR_DENIED;
280 	}
281 
282 	if (!tconf->supported || !tconf->enabled)
283 		return SBI_ERR_NOT_SUPPORTED;
284 
285 	*conf = tconf;
286 
287 	return SBI_SUCCESS;
288 }
289 
290 static int kvm_sbi_fwft_set(struct kvm_vcpu *vcpu, u32 feature,
291 			    unsigned long value, unsigned long flags)
292 {
293 	int ret;
294 	struct kvm_sbi_fwft_config *conf;
295 
296 	ret = kvm_fwft_get_feature(vcpu, feature, &conf);
297 	if (ret)
298 		return ret;
299 
300 	if ((flags & ~SBI_FWFT_SET_FLAG_LOCK) != 0)
301 		return SBI_ERR_INVALID_PARAM;
302 
303 	if (conf->flags & SBI_FWFT_SET_FLAG_LOCK)
304 		return SBI_ERR_DENIED_LOCKED;
305 
306 	conf->flags = flags;
307 
308 	return conf->feature->set(vcpu, conf, false, value);
309 }
310 
311 static int kvm_sbi_fwft_get(struct kvm_vcpu *vcpu, unsigned long feature,
312 			    unsigned long *value)
313 {
314 	int ret;
315 	struct kvm_sbi_fwft_config *conf;
316 
317 	ret = kvm_fwft_get_feature(vcpu, feature, &conf);
318 	if (ret)
319 		return ret;
320 
321 	return conf->feature->get(vcpu, conf, false, value);
322 }
323 
324 static int kvm_sbi_ext_fwft_handler(struct kvm_vcpu *vcpu, struct kvm_run *run,
325 				    struct kvm_vcpu_sbi_return *retdata)
326 {
327 	int ret;
328 	struct kvm_cpu_context *cp = &vcpu->arch.guest_context;
329 	unsigned long funcid = cp->a6;
330 
331 	switch (funcid) {
332 	case SBI_EXT_FWFT_SET:
333 		ret = kvm_sbi_fwft_set(vcpu, cp->a0, cp->a1, cp->a2);
334 		break;
335 	case SBI_EXT_FWFT_GET:
336 		ret = kvm_sbi_fwft_get(vcpu, cp->a0, &retdata->out_val);
337 		break;
338 	default:
339 		ret = SBI_ERR_NOT_SUPPORTED;
340 		break;
341 	}
342 
343 	retdata->err_val = ret;
344 
345 	return 0;
346 }
347 
348 static int kvm_sbi_ext_fwft_init(struct kvm_vcpu *vcpu)
349 {
350 	struct kvm_sbi_fwft *fwft = vcpu_to_fwft(vcpu);
351 	const struct kvm_sbi_fwft_feature *feature;
352 	struct kvm_sbi_fwft_config *conf;
353 	int i;
354 
355 	fwft->configs = kcalloc(ARRAY_SIZE(features), sizeof(struct kvm_sbi_fwft_config),
356 				GFP_KERNEL);
357 	if (!fwft->configs)
358 		return -ENOMEM;
359 
360 	for (i = 0; i < ARRAY_SIZE(features); i++) {
361 		feature = &features[i];
362 		conf = &fwft->configs[i];
363 		if (feature->supported)
364 			conf->supported = feature->supported(vcpu);
365 		else
366 			conf->supported = true;
367 
368 		conf->enabled = conf->supported;
369 		conf->feature = feature;
370 	}
371 
372 	return 0;
373 }
374 
375 static void kvm_sbi_ext_fwft_deinit(struct kvm_vcpu *vcpu)
376 {
377 	struct kvm_sbi_fwft *fwft = vcpu_to_fwft(vcpu);
378 
379 	kfree(fwft->configs);
380 }
381 
382 static void kvm_sbi_ext_fwft_reset(struct kvm_vcpu *vcpu)
383 {
384 	struct kvm_sbi_fwft *fwft = vcpu_to_fwft(vcpu);
385 	const struct kvm_sbi_fwft_feature *feature;
386 	int i;
387 
388 	for (i = 0; i < ARRAY_SIZE(features); i++) {
389 		fwft->configs[i].flags = 0;
390 		feature = &features[i];
391 		if (feature->reset)
392 			feature->reset(vcpu);
393 	}
394 }
395 
396 static unsigned long kvm_sbi_ext_fwft_get_reg_count(struct kvm_vcpu *vcpu)
397 {
398 	unsigned long max_reg_count = sizeof(struct kvm_riscv_sbi_fwft) / sizeof(unsigned long);
399 	const struct kvm_sbi_fwft_feature *feature;
400 	struct kvm_sbi_fwft_config *conf;
401 	unsigned long reg, ret = 0;
402 
403 	for (reg = 0; reg < max_reg_count; reg++) {
404 		feature = kvm_sbi_fwft_regnum_to_feature(reg);
405 		if (!feature)
406 			continue;
407 
408 		conf = kvm_sbi_fwft_get_config(vcpu, feature->id);
409 		if (!conf || !conf->supported)
410 			continue;
411 
412 		ret++;
413 	}
414 
415 	return ret;
416 }
417 
418 static int kvm_sbi_ext_fwft_get_reg_id(struct kvm_vcpu *vcpu, int index, u64 *reg_id)
419 {
420 	int reg, max_reg_count = sizeof(struct kvm_riscv_sbi_fwft) / sizeof(unsigned long);
421 	const struct kvm_sbi_fwft_feature *feature;
422 	struct kvm_sbi_fwft_config *conf;
423 	int idx = 0;
424 
425 	for (reg = 0; reg < max_reg_count; reg++) {
426 		feature = kvm_sbi_fwft_regnum_to_feature(reg);
427 		if (!feature)
428 			continue;
429 
430 		conf = kvm_sbi_fwft_get_config(vcpu, feature->id);
431 		if (!conf || !conf->supported)
432 			continue;
433 
434 		if (index == idx) {
435 			*reg_id = KVM_REG_RISCV |
436 				  (IS_ENABLED(CONFIG_32BIT) ?
437 				   KVM_REG_SIZE_U32 : KVM_REG_SIZE_U64) |
438 				  KVM_REG_RISCV_SBI_STATE |
439 				  KVM_REG_RISCV_SBI_FWFT | reg;
440 			return 0;
441 		}
442 
443 		idx++;
444 	}
445 
446 	return -ENOENT;
447 }
448 
449 static int kvm_sbi_ext_fwft_get_reg(struct kvm_vcpu *vcpu, unsigned long reg_num,
450 				    unsigned long reg_size, void *reg_val)
451 {
452 	const struct kvm_sbi_fwft_feature *feature;
453 	struct kvm_sbi_fwft_config *conf;
454 	unsigned long *value;
455 	int ret = 0;
456 
457 	if (reg_size != sizeof(unsigned long))
458 		return -EINVAL;
459 	value = reg_val;
460 
461 	feature = kvm_sbi_fwft_regnum_to_feature(reg_num);
462 	if (!feature)
463 		return -ENOENT;
464 
465 	conf = kvm_sbi_fwft_get_config(vcpu, feature->id);
466 	if (!conf || !conf->supported)
467 		return -ENOENT;
468 
469 	switch (reg_num - feature->first_reg_num) {
470 	case 0:
471 		*value = conf->enabled;
472 		break;
473 	case 1:
474 		*value = conf->flags;
475 		break;
476 	case 2:
477 		ret = conf->feature->get(vcpu, conf, true, value);
478 		break;
479 	default:
480 		return -ENOENT;
481 	}
482 
483 	return sbi_err_map_linux_errno(ret);
484 }
485 
486 static int kvm_sbi_ext_fwft_set_reg(struct kvm_vcpu *vcpu, unsigned long reg_num,
487 				    unsigned long reg_size, const void *reg_val)
488 {
489 	const struct kvm_sbi_fwft_feature *feature;
490 	struct kvm_sbi_fwft_config *conf;
491 	unsigned long value;
492 	int ret = 0;
493 
494 	if (reg_size != sizeof(unsigned long))
495 		return -EINVAL;
496 	value = *(const unsigned long *)reg_val;
497 
498 	feature = kvm_sbi_fwft_regnum_to_feature(reg_num);
499 	if (!feature)
500 		return -ENOENT;
501 
502 	conf = kvm_sbi_fwft_get_config(vcpu, feature->id);
503 	if (!conf || !conf->supported)
504 		return -ENOENT;
505 
506 	switch (reg_num - feature->first_reg_num) {
507 	case 0:
508 		switch (value) {
509 		case 0:
510 			conf->enabled = false;
511 			break;
512 		case 1:
513 			conf->enabled = true;
514 			break;
515 		default:
516 			return -EINVAL;
517 		}
518 		break;
519 	case 1:
520 		conf->flags = value & SBI_FWFT_SET_FLAG_LOCK;
521 		break;
522 	case 2:
523 		ret = conf->feature->set(vcpu, conf, true, value);
524 		break;
525 	default:
526 		return -ENOENT;
527 	}
528 
529 	return sbi_err_map_linux_errno(ret);
530 }
531 
532 const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_fwft = {
533 	.extid_start = SBI_EXT_FWFT,
534 	.extid_end = SBI_EXT_FWFT,
535 	.handler = kvm_sbi_ext_fwft_handler,
536 	.init = kvm_sbi_ext_fwft_init,
537 	.deinit = kvm_sbi_ext_fwft_deinit,
538 	.reset = kvm_sbi_ext_fwft_reset,
539 	.state_reg_subtype = KVM_REG_RISCV_SBI_FWFT,
540 	.get_state_reg_count = kvm_sbi_ext_fwft_get_reg_count,
541 	.get_state_reg_id = kvm_sbi_ext_fwft_get_reg_id,
542 	.get_state_reg = kvm_sbi_ext_fwft_get_reg,
543 	.set_state_reg = kvm_sbi_ext_fwft_set_reg,
544 };
545