Most implementations cache the combined result of two-stage translation, but some, like Andes cores, use split TLBs that store VS-stage and G-stage entries separately. On such systems, when a VCPU migrates to another CPU, an additional HFENCE.VVMA is required to avoid using stale VS-stage entries, which could otherwise cause guest faults. Introduce a static key to identify CPUs with split two-stage TLBs. When enabled, KVM issues an extra HFENCE.VVMA on VCPU migration to prevent stale VS-stage mappings. Signed-off-by: Hui Min Mina Chou Signed-off-by: Ben Zong-You Xie Reviewed-by: Radim Krčmář --- Changelog: v4: - Rename the patch subject - Remove the Fixes tag - Add a static key so that HFENCE.VVMA is issued only on CPUs with split two-stage TLBs - Add kvm_riscv_setup_vendor_features() to detect mvendorid/marchid and enable the key when required v3: - Resolved build warning; updated header declaration and call side to kvm_riscv_local_tlb_sanitize - Add Radim Krčmář's Reviewed-by tag (https://lore.kernel.org/all/20251023032517.2527193-1-minachou@andestech.com/) v2: - Updated Fixes commit to 92e450507d56 - Renamed function to kvm_riscv_local_tlb_sanitize (https://lore.kernel.org/all/20251021083105.4029305-1-minachou@andestech.com/) --- arch/riscv/include/asm/kvm_host.h | 2 ++ arch/riscv/include/asm/kvm_vmid.h | 2 +- arch/riscv/kvm/main.c | 14 ++++++++++++++ arch/riscv/kvm/vcpu.c | 2 +- arch/riscv/kvm/vmid.c | 6 +++++- 5 files changed, 23 insertions(+), 3 deletions(-) diff --git a/arch/riscv/include/asm/kvm_host.h b/arch/riscv/include/asm/kvm_host.h index d71d3299a335..21abac2f804e 100644 --- a/arch/riscv/include/asm/kvm_host.h +++ b/arch/riscv/include/asm/kvm_host.h @@ -323,4 +323,6 @@ bool kvm_riscv_vcpu_stopped(struct kvm_vcpu *vcpu); void kvm_riscv_vcpu_record_steal_time(struct kvm_vcpu *vcpu); +DECLARE_STATIC_KEY_FALSE(kvm_riscv_tlb_split_mode); + #endif /* __RISCV_KVM_HOST_H__ */ diff --git a/arch/riscv/include/asm/kvm_vmid.h b/arch/riscv/include/asm/kvm_vmid.h index ab98e1434fb7..75fb6e872ccd 100644 --- a/arch/riscv/include/asm/kvm_vmid.h +++ b/arch/riscv/include/asm/kvm_vmid.h @@ -22,6 +22,6 @@ unsigned long kvm_riscv_gstage_vmid_bits(void); int kvm_riscv_gstage_vmid_init(struct kvm *kvm); bool kvm_riscv_gstage_vmid_ver_changed(struct kvm_vmid *vmid); void kvm_riscv_gstage_vmid_update(struct kvm_vcpu *vcpu); -void kvm_riscv_gstage_vmid_sanitize(struct kvm_vcpu *vcpu); +void kvm_riscv_local_tlb_sanitize(struct kvm_vcpu *vcpu); #endif diff --git a/arch/riscv/kvm/main.c b/arch/riscv/kvm/main.c index 67c876de74ef..bf0e4f1abe0f 100644 --- a/arch/riscv/kvm/main.c +++ b/arch/riscv/kvm/main.c @@ -15,6 +15,18 @@ #include #include +DEFINE_STATIC_KEY_FALSE(kvm_riscv_tlb_split_mode); + +static void kvm_riscv_setup_vendor_features(void) +{ + /* Andes AX66: split two-stage TLBs */ + if (riscv_cached_mvendorid(0) == ANDES_VENDOR_ID && + (riscv_cached_marchid(0) & 0xFFFF) == 0x8A66) { + static_branch_enable(&kvm_riscv_tlb_split_mode); + kvm_info("using split two-stage TLBs requiring extra HFENCE.VVMA\n"); + } +} + long kvm_arch_dev_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg) { @@ -159,6 +171,8 @@ static int __init riscv_kvm_init(void) kvm_info("AIA available with %d guest external interrupts\n", kvm_riscv_aia_nr_hgei); + kvm_riscv_setup_vendor_features(); + kvm_register_perf_callbacks(NULL); rc = kvm_init(sizeof(struct kvm_vcpu), 0, THIS_MODULE); diff --git a/arch/riscv/kvm/vcpu.c b/arch/riscv/kvm/vcpu.c index 3ebcfffaa978..796218e4a462 100644 --- a/arch/riscv/kvm/vcpu.c +++ b/arch/riscv/kvm/vcpu.c @@ -968,7 +968,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu) * Note: This should be done after G-stage VMID has been * updated using kvm_riscv_gstage_vmid_ver_changed() */ - kvm_riscv_gstage_vmid_sanitize(vcpu); + kvm_riscv_local_tlb_sanitize(vcpu); trace_kvm_entry(vcpu); diff --git a/arch/riscv/kvm/vmid.c b/arch/riscv/kvm/vmid.c index 3b426c800480..1dbd50c67a88 100644 --- a/arch/riscv/kvm/vmid.c +++ b/arch/riscv/kvm/vmid.c @@ -125,7 +125,7 @@ void kvm_riscv_gstage_vmid_update(struct kvm_vcpu *vcpu) kvm_make_request(KVM_REQ_UPDATE_HGATP, v); } -void kvm_riscv_gstage_vmid_sanitize(struct kvm_vcpu *vcpu) +void kvm_riscv_local_tlb_sanitize(struct kvm_vcpu *vcpu) { unsigned long vmid; @@ -146,4 +146,8 @@ void kvm_riscv_gstage_vmid_sanitize(struct kvm_vcpu *vcpu) vmid = READ_ONCE(vcpu->kvm->arch.vmid.vmid); kvm_riscv_local_hfence_gvma_vmid_all(vmid); + + /* For split TLB designs, flush VS-stage entries also */ + if (static_branch_unlikely(&kvm_riscv_tlb_split_mode)) + kvm_riscv_local_hfence_vvma_all(vmid); } -- 2.34.1