From: David Woodhouse <[email protected]>

The Hyper-V TLB flush hypercalls resolve their target vCPUs through
the plane-0 array, so a flush issued from a non-zero plane flushed
only the plane-0 vCPUs and the caller's own plane kept stale
translations. The Windows secure kernel repopulated a PFN-database
page table entry, flushed, then took a fatal page fault on the very
address it had just mapped, because VTL1's TLB still held the old
read-only zero-page translation.

Resolve each flush target to the vCPU of the calling plane (falling
back to plane 0 where no such plane vCPU exists — it then has no TLB
to flush either) and raise the flush request on that vCPU directly.

Signed-off-by: David Woodhouse <[email protected]>
Assisted-by: LLM
---
 arch/x86/kvm/hyperv.c | 34 ++++++++++++++++++++++++++++++++--
 1 file changed, 32 insertions(+), 2 deletions(-)

diff --git a/arch/x86/kvm/hyperv.c b/arch/x86/kvm/hyperv.c
index eefc18c750f1..797c5cbfbbaf 100644
--- a/arch/x86/kvm/hyperv.c
+++ b/arch/x86/kvm/hyperv.c
@@ -2122,6 +2122,23 @@ static int kvm_hv_hypercall_check_params(struct kvm_vcpu 
*vcpu,
        return 0;
 }
 
+/*
+ * A TLB flush hypercall flushes the caller's VTL view of the target VPs:
+ * resolve the target to the vCPU of the caller's plane, which has its own
+ * roots and TLB tag.  Fall back to plane 0 when the plane vCPU does not
+ * exist (it then has no TLB to flush either).
+ */
+static struct kvm_vcpu *hv_flush_plane_vcpu(struct kvm_vcpu *caller,
+                                           struct kvm_vcpu *v)
+{
+       struct kvm_vcpu *pv;
+
+       if (!caller->plane_level)
+               return v;
+       pv = v->common->vcpus[caller->plane_level];
+       return pv ?: v;
+}
+
 static s64 kvm_hv_flush_tlb(struct kvm_vcpu *vcpu, struct kvm_hv_hcall *hc)
 {
        struct kvm_vcpu_hv *hv_vcpu = to_hv_vcpu(vcpu);
@@ -2257,12 +2274,18 @@ static s64 kvm_hv_flush_tlb(struct kvm_vcpu *vcpu, 
struct kvm_hv_hcall *hc)
         */
        if (all_cpus && !is_guest_mode(vcpu)) {
                kvm_for_each_vcpu(i, v, kvm) {
+                       v = hv_flush_plane_vcpu(vcpu, v);
                        tlb_flush_fifo = kvm_hv_get_tlb_flush_fifo(v, false);
                        hv_tlb_flush_enqueue(v, tlb_flush_fifo,
                                             tlb_flush_entries, hc->rep_cnt);
+                       if (vcpu->plane_level) {
+                               kvm_make_request(KVM_REQ_HV_TLB_FLUSH, v);
+                               kvm_vcpu_kick(v);
+                       }
                }
 
-               kvm_make_all_cpus_request(kvm, KVM_REQ_HV_TLB_FLUSH);
+               if (!vcpu->plane_level)
+                       kvm_make_all_cpus_request(kvm, KVM_REQ_HV_TLB_FLUSH);
        } else if (!is_guest_mode(vcpu)) {
                sparse_set_to_vcpu_mask(kvm, sparse_banks, valid_bank_mask, 
vcpu_mask);
 
@@ -2270,12 +2293,19 @@ static s64 kvm_hv_flush_tlb(struct kvm_vcpu *vcpu, 
struct kvm_hv_hcall *hc)
                        v = kvm_get_vcpu(kvm, i);
                        if (!v)
                                continue;
+                       v = hv_flush_plane_vcpu(vcpu, v);
                        tlb_flush_fifo = kvm_hv_get_tlb_flush_fifo(v, false);
                        hv_tlb_flush_enqueue(v, tlb_flush_fifo,
                                             tlb_flush_entries, hc->rep_cnt);
+                       if (vcpu->plane_level) {
+                               kvm_make_request(KVM_REQ_HV_TLB_FLUSH, v);
+                               kvm_vcpu_kick(v);
+                       }
                }
 
-               kvm_make_vcpus_request_mask(kvm, KVM_REQ_HV_TLB_FLUSH, 
vcpu_mask);
+               if (!vcpu->plane_level)
+                       kvm_make_vcpus_request_mask(kvm, KVM_REQ_HV_TLB_FLUSH,
+                                                   vcpu_mask);
        } else {
                struct kvm_vcpu_hv *hv_v;
 
-- 
2.50.1


Reply via email to