From: Mushahid Hussain <[email protected]>

HV_X64_MSR_REFERENCE_TSC is private to each Virtual Trust Level: the
secure VTL maps its own page.  The reference time behind the pages is
one clock per partition, so every page must show the same scale, offset
and sequence.

KVM keeps a single page address per VM.  A write from another plane
replaces it, the old page stops being updated and the new page starts a
sequence of its own.  Keep the MSR value per plane and write the shared
page contents to every enabled page.  The lowest enabled plane owns the
sequence number; disabling one plane's page keeps the others updated.

Assisted-by: LLM
[dwmw2: our tree already banks the MSR per plane (fbdffa08a242) with a
  one-shot copy for higher planes; replace that with the maintained
  pages, keeping the bank in kvm_hv rather than kvm_arch_plane. ]
Signed-off-by: David Woodhouse <[email protected]>
---
 arch/x86/include/asm/kvm_host.h |   6 +-
 arch/x86/kvm/hyperv.c           | 118 +++++++++++++++-----------------
 2 files changed, 58 insertions(+), 66 deletions(-)

diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index 5945b57c091d..d913c68b18a7 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -1255,8 +1255,10 @@ struct kvm_hv {
        u64 hv_guest_os_id[KVM_MAX_PLANES];
        u64 hv_hypercall[KVM_MAX_PLANES];
        bool hv_forward_hcalls;
-       u64 hv_tsc_page;
-       /* Banked REFERENCE_TSC MSR values for non-zero planes (VTLs). */
+       /*
+        * HV_X64_MSR_REFERENCE_TSC is private to each VTL, the reference time
+        * it exposes is not: every enabled page gets the same contents.
+        */
        u64 hv_tsc_page_plane[KVM_MAX_PLANES];
        enum hv_tsc_page_status hv_tsc_page_status;
 
diff --git a/arch/x86/kvm/hyperv.c b/arch/x86/kvm/hyperv.c
index 759589c6de87..2098daede7d3 100644
--- a/arch/x86/kvm/hyperv.c
+++ b/arch/x86/kvm/hyperv.c
@@ -1224,6 +1224,49 @@ static inline bool tsc_page_update_unsafe(struct kvm_hv 
*hv)
                hv->hv_tsc_emulation_control;
 }
 
+static bool hv_tsc_page_gfn(struct kvm_hv *hv, int plane, u64 *gfn)
+{
+       u64 msr = hv->hv_tsc_page_plane[plane];
+
+       if (!(msr & HV_X64_MSR_TSC_REFERENCE_ENABLE))
+               return false;
+
+       *gfn = msr >> HV_X64_MSR_TSC_REFERENCE_ADDRESS_SHIFT;
+       return true;
+}
+
+/* The lowest plane with an enabled page; it owns the sequence number. */
+static bool hv_first_tsc_page_gfn(struct kvm *kvm, u64 *gfn)
+{
+       struct kvm_hv *hv = to_kvm_hv(kvm);
+       int i;
+
+       for (i = 0; i < KVM_MAX_PLANES; i++) {
+               if (hv_tsc_page_gfn(hv, i, gfn))
+                       return true;
+       }
+
+       return false;
+}
+
+/* Write the first @len bytes of the reference page to every enabled plane. */
+static int hv_write_tsc_pages(struct kvm *kvm, size_t len)
+{
+       struct kvm_hv *hv = to_kvm_hv(kvm);
+       u64 gfn;
+       int i;
+
+       for (i = 0; i < KVM_MAX_PLANES; i++) {
+               if (!hv_tsc_page_gfn(hv, i, &gfn))
+                       continue;
+
+               if (kvm_write_guest(kvm, gfn_to_gpa(gfn), &hv->tsc_ref, len))
+                       return -EFAULT;
+       }
+
+       return 0;
+}
+
 void kvm_hv_setup_tsc_page(struct kvm *kvm,
                           struct pvclock_vcpu_time_info *hv_clock)
 {
@@ -1241,10 +1284,9 @@ void kvm_hv_setup_tsc_page(struct kvm *kvm,
            hv->hv_tsc_page_status == HV_TSC_PAGE_UNSET)
                return;
 
-       if (!(hv->hv_tsc_page & HV_X64_MSR_TSC_REFERENCE_ENABLE))
+       if (!hv_first_tsc_page_gfn(kvm, &gfn))
                return;
 
-       gfn = hv->hv_tsc_page >> HV_X64_MSR_TSC_REFERENCE_ADDRESS_SHIFT;
        /*
         * Because the TSC parameters only vary when there is a
         * change in the master clock, do not bother with caching.
@@ -1267,8 +1309,7 @@ void kvm_hv_setup_tsc_page(struct kvm *kvm,
         * guest to use the time reference count MSR.
         */
        hv->tsc_ref.tsc_sequence = 0;
-       if (kvm_write_guest(kvm, gfn_to_gpa(gfn),
-                           &hv->tsc_ref, sizeof(hv->tsc_ref.tsc_sequence)))
+       if (hv_write_tsc_pages(kvm, sizeof(hv->tsc_ref.tsc_sequence)))
                goto out_err;
 
        if (!compute_tsc_page_parameters(hv_clock, &hv->tsc_ref))
@@ -1276,7 +1317,7 @@ void kvm_hv_setup_tsc_page(struct kvm *kvm,
 
        /* Ensure sequence is zero before writing the rest of the struct.  */
        smp_wmb();
-       if (kvm_write_guest(kvm, gfn_to_gpa(gfn), &hv->tsc_ref, 
sizeof(hv->tsc_ref)))
+       if (hv_write_tsc_pages(kvm, sizeof(hv->tsc_ref)))
                goto out_err;
 
        /*
@@ -1290,8 +1331,7 @@ void kvm_hv_setup_tsc_page(struct kvm *kvm,
        smp_wmb();
 
        hv->tsc_ref.tsc_sequence = tsc_seq;
-       if (kvm_write_guest(kvm, gfn_to_gpa(gfn),
-                           &hv->tsc_ref, sizeof(hv->tsc_ref.tsc_sequence)))
+       if (hv_write_tsc_pages(kvm, sizeof(hv->tsc_ref.tsc_sequence)))
                goto out_err;
 
        hv->tsc_ref_from_guest = false;
@@ -1499,61 +1539,12 @@ static int kvm_hv_set_msr_pw(struct kvm_vcpu *vcpu, u32 
msr, u64 data,
                hv->hv_hypercall[vcpu->plane_level] = data;
                break;
        }
-       case HV_X64_MSR_REFERENCE_TSC:
-               /*
-                * The reference TSC page is a per-VTL instance.  Plane 0 keeps
-                * the live masterclock-maintained page; a non-zero plane gets
-                * a one-shot copy of the current parameters, which stay valid
-                * while the masterclock is stable.
-                */
-               if (vcpu->plane_level) {
-                       struct ms_hyperv_tsc_page tsc_ref = {};
-                       u32 tsc_seq;
-                       u64 gfn;
-
-                       hv->hv_tsc_page_plane[vcpu->plane_level] = data;
-                       if (!(data & HV_X64_MSR_TSC_REFERENCE_ENABLE))
-                               break;
-
-                       /*
-                        * Copy only host-computed parameters.  After a
-                        * masterclock change under guest TSC emulation,
-                        * hv->tsc_ref holds values read back from the
-                        * plane-0 page, which the lowest VTL can write;
-                        * presenting those to another plane would let it
-                        * forge that plane's clock.  Without usable
-                        * parameters, publish an invalid (zero) sequence so
-                        * the plane falls back to the TIME_REF_COUNT MSR.
-                        */
-                       if (hv->hv_tsc_page_status == HV_TSC_PAGE_SET &&
-                           !hv->tsc_ref_from_guest)
-                               tsc_ref = hv->tsc_ref;
-
-                       gfn = data >> HV_X64_MSR_TSC_REFERENCE_ADDRESS_SHIFT;
-                       tsc_seq = tsc_ref.tsc_sequence;
-
-                       /*
-                        * Write the body under a zero sequence, then the
-                        * real sequence, so a concurrent reader on this
-                        * plane never consumes torn parameters.
-                        */
-                       tsc_ref.tsc_sequence = 0;
-                       if (kvm_vcpu_write_guest(vcpu, gfn_to_gpa(gfn),
-                                                &tsc_ref, sizeof(tsc_ref)))
-                               return 1;
-
-                       if (!tsc_seq)
-                               break;
+       case HV_X64_MSR_REFERENCE_TSC: {
+               u64 gfn;
 
-                       tsc_ref.tsc_sequence = tsc_seq;
-                       if (kvm_vcpu_write_guest(vcpu, gfn_to_gpa(gfn),
-                                                &tsc_ref,
-                                                sizeof(tsc_ref.tsc_sequence)))
-                               return 1;
-                       break;
-               }
-               hv->hv_tsc_page = data;
-               if (hv->hv_tsc_page & HV_X64_MSR_TSC_REFERENCE_ENABLE) {
+               hv->hv_tsc_page_plane[vcpu->plane_level] = data;
+               /* Another plane's page may still be enabled: keep it updated. 
*/
+               if (hv_first_tsc_page_gfn(kvm, &gfn)) {
                        if (!host)
                                hv->hv_tsc_page_status = 
HV_TSC_PAGE_GUEST_CHANGED;
                        else
@@ -1563,6 +1554,7 @@ static int kvm_hv_set_msr_pw(struct kvm_vcpu *vcpu, u32 
msr, u64 data,
                        hv->hv_tsc_page_status = HV_TSC_PAGE_UNSET;
                }
                break;
+       }
        case HV_X64_MSR_CRASH_P0 ... HV_X64_MSR_CRASH_P4:
                return kvm_hv_msr_set_crash_data(kvm,
                                                 msr - HV_X64_MSR_CRASH_P0,
@@ -1786,9 +1778,7 @@ static int kvm_hv_get_msr_pw(struct kvm_vcpu *vcpu, u32 
msr, u64 *pdata,
                data = get_time_ref_counter(kvm);
                break;
        case HV_X64_MSR_REFERENCE_TSC:
-               data = vcpu->plane_level ?
-                      hv->hv_tsc_page_plane[vcpu->plane_level] :
-                      hv->hv_tsc_page;
+               data = hv->hv_tsc_page_plane[vcpu->plane_level];
                break;
        case HV_X64_MSR_CRASH_P0 ... HV_X64_MSR_CRASH_P4:
                return kvm_hv_msr_get_crash_data(kvm,
-- 
2.50.1


Reply via email to