Fix a bug where FWFT features could be incorrectly exposed to guests after userspace disables their dependent ISA extensions at runtime. The 'supported' field in kvm_sbi_fwft_config was set once during vCPU initialization based on the initial hardware/extension availability. However, when userspace subsequently disables ISA extensions via the KVM ONE_REG interface, the 'supported' field was not updated. This caused the following issues: 1. FWFT features would remain visible and accessible to guests even after their prerequisite ISA extensions were disabled 2. Guests could configure FWFT features that depend on disabled extensions, leading to undefined behavior 3. The static 'supported' flag and the dynamic supported() callback could disagree about feature availability The fix introduces a two-layer checking mechanism: 1. Add an optional init() callback to the kvm_sbi_fwft_feature structure for features that require hardware probing during initialization. This separates the one-time hardware detection logic from the runtime availability check. 2. Add runtime checks in all FWFT-related functions that call feature->supported(vcpu) if the callback exists. This ensures feature availability is re-evaluated based on the current ISA extension state. This approach maintains the cached 'supported' field for initialization- time decisions while ensuring runtime availability is always determined by the current vCPU configuration, not initialization-time snapshots. Fixes: 6b72fd170592 ("RISC-V: KVM: add support for FWFT SBI extension") Signed-off-by: Yong-Xuan Wang --- arch/riscv/kvm/vcpu_sbi_fwft.c | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/arch/riscv/kvm/vcpu_sbi_fwft.c b/arch/riscv/kvm/vcpu_sbi_fwft.c index 2eab15339694..dde6e39aab5e 100644 --- a/arch/riscv/kvm/vcpu_sbi_fwft.c +++ b/arch/riscv/kvm/vcpu_sbi_fwft.c @@ -35,6 +35,16 @@ struct kvm_sbi_fwft_feature { */ bool (*supported)(struct kvm_vcpu *vcpu); + /** + * @init: Probe and initialize the feature on the vcpu + * + * This callback is optional. If provided, it will be called during + * vcpu initialization to probe the feature availability and perform + * any necessary initialization. Returns true if the feature is supported + * and initialized successfully, false otherwise. + */ + bool (*init)(struct kvm_vcpu *vcpu); + /** * @reset: Reset the feature value irrespective whether feature is supported or not * @@ -137,10 +147,12 @@ static bool try_to_set_pmm(unsigned long value) static bool kvm_sbi_fwft_pointer_masking_pmlen_supported(struct kvm_vcpu *vcpu) { - struct kvm_sbi_fwft *fwft = vcpu_to_fwft(vcpu); + return riscv_isa_extension_available(vcpu->arch.isa, SMNPM); +} - if (!riscv_isa_extension_available(vcpu->arch.isa, SMNPM)) - return false; +static bool kvm_sbi_fwft_pointer_masking_pmlen_init(struct kvm_vcpu *vcpu) +{ + struct kvm_sbi_fwft *fwft = vcpu_to_fwft(vcpu); fwft->have_vs_pmlen_7 = try_to_set_pmm(ENVCFG_PMM_PMLEN_7); fwft->have_vs_pmlen_16 = try_to_set_pmm(ENVCFG_PMM_PMLEN_16); @@ -231,6 +243,7 @@ static const struct kvm_sbi_fwft_feature features[] = { .first_reg_num = offsetof(struct kvm_riscv_sbi_fwft, pointer_masking.enable) / sizeof(unsigned long), .supported = kvm_sbi_fwft_pointer_masking_pmlen_supported, + .init = kvm_sbi_fwft_pointer_masking_pmlen_init, .reset = kvm_sbi_fwft_reset_pointer_masking_pmlen, .set = kvm_sbi_fwft_set_pointer_masking_pmlen, .get = kvm_sbi_fwft_get_pointer_masking_pmlen, @@ -281,6 +294,8 @@ static int kvm_fwft_get_feature(struct kvm_vcpu *vcpu, u32 feature, if (!tconf->supported || !tconf->enabled) return SBI_ERR_NOT_SUPPORTED; + else if (tconf->feature->supported && !tconf->feature->supported(vcpu)) + return SBI_ERR_NOT_SUPPORTED; *conf = tconf; @@ -365,6 +380,9 @@ static int kvm_sbi_ext_fwft_init(struct kvm_vcpu *vcpu) else conf->supported = true; + if (conf->supported && feature->init) + conf->supported = feature->init(vcpu); + conf->enabled = conf->supported; conf->feature = feature; } @@ -408,6 +426,8 @@ static unsigned long kvm_sbi_ext_fwft_get_reg_count(struct kvm_vcpu *vcpu) conf = kvm_sbi_fwft_get_config(vcpu, feature->id); if (!conf || !conf->supported) continue; + else if (conf->feature->supported && !conf->feature->supported(vcpu)) + continue; ret++; } @@ -430,6 +450,8 @@ static int kvm_sbi_ext_fwft_get_reg_id(struct kvm_vcpu *vcpu, int index, u64 *re conf = kvm_sbi_fwft_get_config(vcpu, feature->id); if (!conf || !conf->supported) continue; + else if (conf->feature->supported && !conf->feature->supported(vcpu)) + continue; if (index == idx) { *reg_id = KVM_REG_RISCV | @@ -465,6 +487,8 @@ static int kvm_sbi_ext_fwft_get_reg(struct kvm_vcpu *vcpu, unsigned long reg_num conf = kvm_sbi_fwft_get_config(vcpu, feature->id); if (!conf || !conf->supported) return -ENOENT; + else if (conf->feature->supported && !conf->feature->supported(vcpu)) + return -ENOENT; switch (reg_num - feature->first_reg_num) { case 0: @@ -502,6 +526,8 @@ static int kvm_sbi_ext_fwft_set_reg(struct kvm_vcpu *vcpu, unsigned long reg_num conf = kvm_sbi_fwft_get_config(vcpu, feature->id); if (!conf || !conf->supported) return -ENOENT; + else if (conf->feature->supported && !conf->feature->supported(vcpu)) + return -ENOENT; switch (reg_num - feature->first_reg_num) { case 0: -- 2.43.7