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
kvm_fwft_is_defined_feature(enum sbi_fwft_feature_t feature)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
kvm_sbi_fwft_misaligned_delegation_supported(struct kvm_vcpu * vcpu)87 static bool kvm_sbi_fwft_misaligned_delegation_supported(struct kvm_vcpu *vcpu)
88 {
89 return misaligned_traps_can_delegate();
90 }
91
kvm_sbi_fwft_reset_misaligned_delegation(struct kvm_vcpu * vcpu)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
kvm_sbi_fwft_set_misaligned_delegation(struct kvm_vcpu * vcpu,struct kvm_sbi_fwft_config * conf,bool one_reg_access,unsigned long value)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
kvm_sbi_fwft_get_misaligned_delegation(struct kvm_vcpu * vcpu,struct kvm_sbi_fwft_config * conf,bool one_reg_access,unsigned long * value)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
try_to_set_pmm(unsigned long value)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
kvm_sbi_fwft_pointer_masking_pmlen_supported(struct kvm_vcpu * vcpu)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
kvm_sbi_fwft_reset_pointer_masking_pmlen(struct kvm_vcpu * vcpu)151 static void kvm_sbi_fwft_reset_pointer_masking_pmlen(struct kvm_vcpu *vcpu)
152 {
153 vcpu->arch.cfg.henvcfg &= ~ENVCFG_PMM;
154 }
155
kvm_sbi_fwft_set_pointer_masking_pmlen(struct kvm_vcpu * vcpu,struct kvm_sbi_fwft_config * conf,bool one_reg_access,unsigned long value)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
kvm_sbi_fwft_get_pointer_masking_pmlen(struct kvm_vcpu * vcpu,struct kvm_sbi_fwft_config * conf,bool one_reg_access,unsigned long * value)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
kvm_sbi_fwft_regnum_to_feature(unsigned long reg_num)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 *
kvm_sbi_fwft_get_config(struct kvm_vcpu * vcpu,enum sbi_fwft_feature_t feature)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
kvm_fwft_get_feature(struct kvm_vcpu * vcpu,u32 feature,struct kvm_sbi_fwft_config ** conf)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
kvm_sbi_fwft_set(struct kvm_vcpu * vcpu,u32 feature,unsigned long value,unsigned long flags)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
kvm_sbi_fwft_get(struct kvm_vcpu * vcpu,unsigned long feature,unsigned long * value)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
kvm_sbi_ext_fwft_handler(struct kvm_vcpu * vcpu,struct kvm_run * run,struct kvm_vcpu_sbi_return * retdata)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
kvm_sbi_ext_fwft_init(struct kvm_vcpu * vcpu)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 = kzalloc_objs(struct kvm_sbi_fwft_config,
356 ARRAY_SIZE(features));
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
kvm_sbi_ext_fwft_deinit(struct kvm_vcpu * vcpu)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
kvm_sbi_ext_fwft_reset(struct kvm_vcpu * vcpu)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
kvm_sbi_ext_fwft_get_reg_count(struct kvm_vcpu * vcpu)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
kvm_sbi_ext_fwft_get_reg_id(struct kvm_vcpu * vcpu,int index,u64 * reg_id)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
kvm_sbi_ext_fwft_get_reg(struct kvm_vcpu * vcpu,unsigned long reg_num,unsigned long reg_size,void * reg_val)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
kvm_sbi_ext_fwft_set_reg(struct kvm_vcpu * vcpu,unsigned long reg_num,unsigned long reg_size,const void * reg_val)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