Add an optional init() callback to separate one-time hardware probing
from runtime availability checks. For pointer masking, this allows
probing supported PMM lengths during initialization while checking ISA
extension availability at runtime.

Fix try_to_set_pmm() to restore the previous HENVCFG.PMM value after
probing, preventing side effects from hardware detection. Add preemption
protection to ensure CSR probe sequences complete atomically on the same
CPU.

Fixes: 6f576fc0aeb9 ("RISC-V: KVM: Add support for 
SBI_FWFT_POINTER_MASKING_PMLEN")

Signed-off-by: Yong-Xuan Wang <[email protected]>
---
 arch/riscv/kvm/vcpu_sbi_fwft.c | 33 +++++++++++++++++++++++++++++----
 1 file changed, 29 insertions(+), 4 deletions(-)

diff --git a/arch/riscv/kvm/vcpu_sbi_fwft.c b/arch/riscv/kvm/vcpu_sbi_fwft.c
index 5e4aafb0cbf1..aee951f2b8e6 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
         *
@@ -131,19 +141,30 @@ static long kvm_sbi_fwft_get_misaligned_delegation(struct 
kvm_vcpu *vcpu,
 
 static bool try_to_set_pmm(unsigned long value)
 {
+       unsigned long prev;
+       bool ret;
+
+       prev = csr_read_clear(CSR_HENVCFG, ENVCFG_PMM);
        csr_set(CSR_HENVCFG, value);
-       return (csr_read_clear(CSR_HENVCFG, ENVCFG_PMM) & ENVCFG_PMM) == value;
+       ret = (csr_read_clear(CSR_HENVCFG, ENVCFG_PMM) & ENVCFG_PMM) == value;
+       csr_write(CSR_HENVCFG, prev);
+
+       return ret;
 }
 
 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);
 
+       preempt_disable();
        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);
+       preempt_enable();
 
        return fwft->have_vs_pmlen_7 || fwft->have_vs_pmlen_16;
 }
@@ -231,6 +252,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,
@@ -365,6 +387,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;
        }

-- 
2.43.7


Reply via email to