Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

RISC-V: KVM: Implement ONE_REG interface for SBI FWFT state

The KVM user-space needs a way to save/restore the state of
SBI FWFT features so implement SBI extension ONE_REG callbacks.

Signed-off-by: Anup Patel <apatel@ventanamicro.com>
Reviewed-by: Andrew Jones <ajones@ventanamicro.com>
Link: https://lore.kernel.org/r/20250823155947.1354229-6-apatel@ventanamicro.com
Signed-off-by: Anup Patel <anup@brainfault.org>

authored by

Anup Patel and committed by
Anup Patel
48d67106 85e7850e

+200 -13
+1
arch/riscv/include/asm/kvm_vcpu_sbi_fwft.h
··· 16 16 struct kvm_sbi_fwft_config { 17 17 const struct kvm_sbi_fwft_feature *feature; 18 18 bool supported; 19 + bool enabled; 19 20 unsigned long flags; 20 21 }; 21 22
+15
arch/riscv/include/uapi/asm/kvm.h
··· 220 220 unsigned long shmem_hi; 221 221 }; 222 222 223 + struct kvm_riscv_sbi_fwft_feature { 224 + unsigned long enable; 225 + unsigned long flags; 226 + unsigned long value; 227 + }; 228 + 229 + /* SBI FWFT extension registers for KVM_GET_ONE_REG and KVM_SET_ONE_REG */ 230 + struct kvm_riscv_sbi_fwft { 231 + struct kvm_riscv_sbi_fwft_feature misaligned_deleg; 232 + struct kvm_riscv_sbi_fwft_feature pointer_masking; 233 + }; 234 + 223 235 /* Possible states for kvm_riscv_timer */ 224 236 #define KVM_RISCV_TIMER_STATE_OFF 0 225 237 #define KVM_RISCV_TIMER_STATE_ON 1 ··· 315 303 #define KVM_REG_RISCV_SBI_STA (0x0 << KVM_REG_RISCV_SUBTYPE_SHIFT) 316 304 #define KVM_REG_RISCV_SBI_STA_REG(name) \ 317 305 (offsetof(struct kvm_riscv_sbi_sta, name) / sizeof(unsigned long)) 306 + #define KVM_REG_RISCV_SBI_FWFT (0x1 << KVM_REG_RISCV_SUBTYPE_SHIFT) 307 + #define KVM_REG_RISCV_SBI_FWFT_REG(name) \ 308 + (offsetof(struct kvm_riscv_sbi_fwft, name) / sizeof(unsigned long)) 318 309 319 310 /* Device Control API: RISC-V AIA */ 320 311 #define KVM_DEV_RISCV_APLIC_ALIGN 0x1000
+184 -13
arch/riscv/kvm/vcpu_sbi_fwft.c
··· 23 23 enum sbi_fwft_feature_t id; 24 24 25 25 /** 26 + * @first_reg_num: ONE_REG index of the first ONE_REG register 27 + */ 28 + unsigned long first_reg_num; 29 + 30 + /** 26 31 * @supported: Check if the feature is supported on the vcpu 27 32 * 28 33 * This callback is optional, if not provided the feature is assumed to ··· 49 44 * 50 45 * This callback is mandatory 51 46 */ 52 - long (*set)(struct kvm_vcpu *vcpu, struct kvm_sbi_fwft_config *conf, unsigned long value); 47 + long (*set)(struct kvm_vcpu *vcpu, struct kvm_sbi_fwft_config *conf, 48 + bool one_reg_access, unsigned long value); 53 49 54 50 /** 55 51 * @get: Get the feature current value ··· 59 53 * 60 54 * This callback is mandatory 61 55 */ 62 - long (*get)(struct kvm_vcpu *vcpu, struct kvm_sbi_fwft_config *conf, unsigned long *value); 56 + long (*get)(struct kvm_vcpu *vcpu, struct kvm_sbi_fwft_config *conf, 57 + bool one_reg_access, unsigned long *value); 63 58 }; 64 59 65 60 static const enum sbi_fwft_feature_t kvm_fwft_defined_features[] = { ··· 98 91 99 92 static long kvm_sbi_fwft_set_misaligned_delegation(struct kvm_vcpu *vcpu, 100 93 struct kvm_sbi_fwft_config *conf, 101 - unsigned long value) 94 + bool one_reg_access, unsigned long value) 102 95 { 103 96 struct kvm_vcpu_config *cfg = &vcpu->arch.cfg; 104 97 105 98 if (value == 1) { 106 99 cfg->hedeleg |= MIS_DELEG; 107 - csr_set(CSR_HEDELEG, MIS_DELEG); 100 + if (!one_reg_access) 101 + csr_set(CSR_HEDELEG, MIS_DELEG); 108 102 } else if (value == 0) { 109 103 cfg->hedeleg &= ~MIS_DELEG; 110 - csr_clear(CSR_HEDELEG, MIS_DELEG); 104 + if (!one_reg_access) 105 + csr_clear(CSR_HEDELEG, MIS_DELEG); 111 106 } else { 112 107 return SBI_ERR_INVALID_PARAM; 113 108 } ··· 119 110 120 111 static long kvm_sbi_fwft_get_misaligned_delegation(struct kvm_vcpu *vcpu, 121 112 struct kvm_sbi_fwft_config *conf, 122 - unsigned long *value) 113 + bool one_reg_access, unsigned long *value) 123 114 { 124 - *value = (csr_read(CSR_HEDELEG) & MIS_DELEG) == MIS_DELEG; 115 + struct kvm_vcpu_config *cfg = &vcpu->arch.cfg; 125 116 117 + *value = (cfg->hedeleg & MIS_DELEG) == MIS_DELEG; 126 118 return SBI_SUCCESS; 127 119 } 128 120 ··· 155 145 156 146 static long kvm_sbi_fwft_set_pointer_masking_pmlen(struct kvm_vcpu *vcpu, 157 147 struct kvm_sbi_fwft_config *conf, 158 - unsigned long value) 148 + bool one_reg_access, unsigned long value) 159 149 { 160 150 struct kvm_sbi_fwft *fwft = vcpu_to_fwft(vcpu); 161 151 unsigned long pmm; ··· 186 176 * update here so that VCPU see's pointer masking mode change 187 177 * immediately. 188 178 */ 189 - csr_write(CSR_HENVCFG, vcpu->arch.cfg.henvcfg); 179 + if (!one_reg_access) 180 + csr_write(CSR_HENVCFG, vcpu->arch.cfg.henvcfg); 190 181 191 182 return SBI_SUCCESS; 192 183 } 193 184 194 185 static long kvm_sbi_fwft_get_pointer_masking_pmlen(struct kvm_vcpu *vcpu, 195 186 struct kvm_sbi_fwft_config *conf, 196 - unsigned long *value) 187 + bool one_reg_access, unsigned long *value) 197 188 { 198 189 switch (vcpu->arch.cfg.henvcfg & ENVCFG_PMM) { 199 190 case ENVCFG_PMM_PMLEN_0: ··· 218 207 static const struct kvm_sbi_fwft_feature features[] = { 219 208 { 220 209 .id = SBI_FWFT_MISALIGNED_EXC_DELEG, 210 + .first_reg_num = offsetof(struct kvm_riscv_sbi_fwft, misaligned_deleg.enable) / 211 + sizeof(unsigned long), 221 212 .supported = kvm_sbi_fwft_misaligned_delegation_supported, 222 213 .reset = kvm_sbi_fwft_reset_misaligned_delegation, 223 214 .set = kvm_sbi_fwft_set_misaligned_delegation, ··· 228 215 #ifndef CONFIG_32BIT 229 216 { 230 217 .id = SBI_FWFT_POINTER_MASKING_PMLEN, 218 + .first_reg_num = offsetof(struct kvm_riscv_sbi_fwft, pointer_masking.enable) / 219 + sizeof(unsigned long), 231 220 .supported = kvm_sbi_fwft_pointer_masking_pmlen_supported, 232 221 .reset = kvm_sbi_fwft_reset_pointer_masking_pmlen, 233 222 .set = kvm_sbi_fwft_set_pointer_masking_pmlen, ··· 237 222 }, 238 223 #endif 239 224 }; 225 + 226 + static const struct kvm_sbi_fwft_feature *kvm_sbi_fwft_regnum_to_feature(unsigned long reg_num) 227 + { 228 + const struct kvm_sbi_fwft_feature *feature; 229 + int i; 230 + 231 + for (i = 0; i < ARRAY_SIZE(features); i++) { 232 + feature = &features[i]; 233 + if (feature->first_reg_num <= reg_num && reg_num < (feature->first_reg_num + 3)) 234 + return feature; 235 + } 236 + 237 + return NULL; 238 + } 240 239 241 240 static struct kvm_sbi_fwft_config * 242 241 kvm_sbi_fwft_get_config(struct kvm_vcpu *vcpu, enum sbi_fwft_feature_t feature) ··· 279 250 return SBI_ERR_DENIED; 280 251 } 281 252 282 - if (!tconf->supported) 253 + if (!tconf->supported || !tconf->enabled) 283 254 return SBI_ERR_NOT_SUPPORTED; 284 255 285 256 *conf = tconf; ··· 305 276 306 277 conf->flags = flags; 307 278 308 - return conf->feature->set(vcpu, conf, value); 279 + return conf->feature->set(vcpu, conf, false, value); 309 280 } 310 281 311 282 static int kvm_sbi_fwft_get(struct kvm_vcpu *vcpu, unsigned long feature, ··· 318 289 if (ret) 319 290 return ret; 320 291 321 - return conf->feature->get(vcpu, conf, value); 292 + return conf->feature->get(vcpu, conf, false, value); 322 293 } 323 294 324 295 static int kvm_sbi_ext_fwft_handler(struct kvm_vcpu *vcpu, struct kvm_run *run, ··· 365 336 else 366 337 conf->supported = true; 367 338 339 + conf->enabled = conf->supported; 368 340 conf->feature = feature; 369 341 } 370 342 ··· 393 363 } 394 364 } 395 365 366 + static unsigned long kvm_sbi_ext_fwft_get_reg_count(struct kvm_vcpu *vcpu) 367 + { 368 + unsigned long max_reg_count = sizeof(struct kvm_riscv_sbi_fwft) / sizeof(unsigned long); 369 + const struct kvm_sbi_fwft_feature *feature; 370 + struct kvm_sbi_fwft_config *conf; 371 + unsigned long reg, ret = 0; 372 + 373 + for (reg = 0; reg < max_reg_count; reg++) { 374 + feature = kvm_sbi_fwft_regnum_to_feature(reg); 375 + if (!feature) 376 + continue; 377 + 378 + conf = kvm_sbi_fwft_get_config(vcpu, feature->id); 379 + if (!conf || !conf->supported) 380 + continue; 381 + 382 + ret++; 383 + } 384 + 385 + return ret; 386 + } 387 + 388 + static int kvm_sbi_ext_fwft_get_reg_id(struct kvm_vcpu *vcpu, int index, u64 *reg_id) 389 + { 390 + int reg, max_reg_count = sizeof(struct kvm_riscv_sbi_fwft) / sizeof(unsigned long); 391 + const struct kvm_sbi_fwft_feature *feature; 392 + struct kvm_sbi_fwft_config *conf; 393 + int idx = 0; 394 + 395 + for (reg = 0; reg < max_reg_count; reg++) { 396 + feature = kvm_sbi_fwft_regnum_to_feature(reg); 397 + if (!feature) 398 + continue; 399 + 400 + conf = kvm_sbi_fwft_get_config(vcpu, feature->id); 401 + if (!conf || !conf->supported) 402 + continue; 403 + 404 + if (index == idx) { 405 + *reg_id = KVM_REG_RISCV | 406 + (IS_ENABLED(CONFIG_32BIT) ? 407 + KVM_REG_SIZE_U32 : KVM_REG_SIZE_U64) | 408 + KVM_REG_RISCV_SBI_STATE | 409 + KVM_REG_RISCV_SBI_FWFT | reg; 410 + return 0; 411 + } 412 + 413 + idx++; 414 + } 415 + 416 + return -ENOENT; 417 + } 418 + 419 + static int kvm_sbi_ext_fwft_get_reg(struct kvm_vcpu *vcpu, unsigned long reg_num, 420 + unsigned long reg_size, void *reg_val) 421 + { 422 + const struct kvm_sbi_fwft_feature *feature; 423 + struct kvm_sbi_fwft_config *conf; 424 + unsigned long *value; 425 + int ret = 0; 426 + 427 + if (reg_size != sizeof(unsigned long)) 428 + return -EINVAL; 429 + value = reg_val; 430 + 431 + feature = kvm_sbi_fwft_regnum_to_feature(reg_num); 432 + if (!feature) 433 + return -ENOENT; 434 + 435 + conf = kvm_sbi_fwft_get_config(vcpu, feature->id); 436 + if (!conf || !conf->supported) 437 + return -ENOENT; 438 + 439 + switch (reg_num - feature->first_reg_num) { 440 + case 0: 441 + *value = conf->enabled; 442 + break; 443 + case 1: 444 + *value = conf->flags; 445 + break; 446 + case 2: 447 + ret = conf->feature->get(vcpu, conf, true, value); 448 + break; 449 + default: 450 + return -ENOENT; 451 + } 452 + 453 + return sbi_err_map_linux_errno(ret); 454 + } 455 + 456 + static int kvm_sbi_ext_fwft_set_reg(struct kvm_vcpu *vcpu, unsigned long reg_num, 457 + unsigned long reg_size, const void *reg_val) 458 + { 459 + const struct kvm_sbi_fwft_feature *feature; 460 + struct kvm_sbi_fwft_config *conf; 461 + unsigned long value; 462 + int ret = 0; 463 + 464 + if (reg_size != sizeof(unsigned long)) 465 + return -EINVAL; 466 + value = *(const unsigned long *)reg_val; 467 + 468 + feature = kvm_sbi_fwft_regnum_to_feature(reg_num); 469 + if (!feature) 470 + return -ENOENT; 471 + 472 + conf = kvm_sbi_fwft_get_config(vcpu, feature->id); 473 + if (!conf || !conf->supported) 474 + return -ENOENT; 475 + 476 + switch (reg_num - feature->first_reg_num) { 477 + case 0: 478 + switch (value) { 479 + case 0: 480 + conf->enabled = false; 481 + break; 482 + case 1: 483 + conf->enabled = true; 484 + break; 485 + default: 486 + return -EINVAL; 487 + } 488 + break; 489 + case 1: 490 + conf->flags = value & SBI_FWFT_SET_FLAG_LOCK; 491 + break; 492 + case 2: 493 + ret = conf->feature->set(vcpu, conf, true, value); 494 + break; 495 + default: 496 + return -ENOENT; 497 + } 498 + 499 + return sbi_err_map_linux_errno(ret); 500 + } 501 + 396 502 const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_fwft = { 397 503 .extid_start = SBI_EXT_FWFT, 398 504 .extid_end = SBI_EXT_FWFT, ··· 536 370 .init = kvm_sbi_ext_fwft_init, 537 371 .deinit = kvm_sbi_ext_fwft_deinit, 538 372 .reset = kvm_sbi_ext_fwft_reset, 373 + .state_reg_subtype = KVM_REG_RISCV_SBI_FWFT, 374 + .get_state_reg_count = kvm_sbi_ext_fwft_get_reg_count, 375 + .get_state_reg_id = kvm_sbi_ext_fwft_get_reg_id, 376 + .get_state_reg = kvm_sbi_ext_fwft_get_reg, 377 + .set_state_reg = kvm_sbi_ext_fwft_set_reg, 539 378 };