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