Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  hyperv_clock.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (C) 2021, Red Hat, Inc.
 *
 * Tests for Hyper-V clocksources
 */

#include "test_util.h"
#include "kvm_util.h"
#include "processor.h"
#include "hyperv.h"

struct ms_hyperv_tsc_page {
 volatile u32 tsc_sequence;
 u32 reserved1;
 volatile u64 tsc_scale;
 volatile s64 tsc_offset;
} __packed;

/* Simplified mul_u64_u64_shr() */
static inline u64 mul_u64_u64_shr64(u64 a, u64 b)
{
 union {
  u64 ll;
  struct {
   u32 low, high;
  } l;
 } rm, rn, rh, a0, b0;
 u64 c;

 a0.ll = a;
 b0.ll = b;

 rm.ll = (u64)a0.l.low * b0.l.high;
 rn.ll = (u64)a0.l.high * b0.l.low;
 rh.ll = (u64)a0.l.high * b0.l.high;

 rh.l.low = c = rm.l.high + rn.l.high + rh.l.low;
 rh.l.high = (c >> 32) + rh.l.high;

 return rh.ll;
}

static inline void nop_loop(void)
{
 int i;

 for (i = 0; i < 100000000; i++)
  asm volatile("nop");
}

static inline void check_tsc_msr_rdtsc(void)
{
 u64 tsc_freq, r1, r2, t1, t2;
 s64 delta_ns;

 tsc_freq = rdmsr(HV_X64_MSR_TSC_FREQUENCY);
 GUEST_ASSERT(tsc_freq > 0);

 /* For increased accuracy, take mean rdtsc() before and afrer rdmsr() */
 r1 = rdtsc();
 t1 = rdmsr(HV_X64_MSR_TIME_REF_COUNT);
 r1 = (r1 + rdtsc()) / 2;
 nop_loop();
 r2 = rdtsc();
 t2 = rdmsr(HV_X64_MSR_TIME_REF_COUNT);
 r2 = (r2 + rdtsc()) / 2;

 GUEST_ASSERT(r2 > r1 && t2 > t1);

 /* HV_X64_MSR_TIME_REF_COUNT is in 100ns */
 delta_ns = ((t2 - t1) * 100) - ((r2 - r1) * 1000000000 / tsc_freq);
 if (delta_ns < 0)
  delta_ns = -delta_ns;

 /* 1% tolerance */
 GUEST_ASSERT(delta_ns * 100 < (t2 - t1) * 100);
}

static inline u64 get_tscpage_ts(struct ms_hyperv_tsc_page *tsc_page)
{
 return mul_u64_u64_shr64(rdtsc(), tsc_page->tsc_scale) + tsc_page->tsc_offset;
}

static inline void check_tsc_msr_tsc_page(struct ms_hyperv_tsc_page *tsc_page)
{
 u64 r1, r2, t1, t2;

 /* Compare TSC page clocksource with HV_X64_MSR_TIME_REF_COUNT */
 t1 = get_tscpage_ts(tsc_page);
 r1 = rdmsr(HV_X64_MSR_TIME_REF_COUNT);

 /* 10 ms tolerance */
 GUEST_ASSERT(r1 >= t1 && r1 - t1 < 100000);
 nop_loop();

 t2 = get_tscpage_ts(tsc_page);
 r2 = rdmsr(HV_X64_MSR_TIME_REF_COUNT);
 GUEST_ASSERT(r2 >= t1 && r2 - t2 < 100000);
}

static void guest_main(struct ms_hyperv_tsc_page *tsc_page, vm_paddr_t tsc_page_gpa)
{
 u64 tsc_scale, tsc_offset;

 /* Set Guest OS id to enable Hyper-V emulation */
 GUEST_SYNC(1);
 wrmsr(HV_X64_MSR_GUEST_OS_ID, HYPERV_LINUX_OS_ID);
 GUEST_SYNC(2);

 check_tsc_msr_rdtsc();

 GUEST_SYNC(3);

 /* Set up TSC page is disabled state, check that it's clean */
 wrmsr(HV_X64_MSR_REFERENCE_TSC, tsc_page_gpa);
 GUEST_ASSERT(tsc_page->tsc_sequence == 0);
 GUEST_ASSERT(tsc_page->tsc_scale == 0);
 GUEST_ASSERT(tsc_page->tsc_offset == 0);

 GUEST_SYNC(4);

 /* Set up TSC page is enabled state */
 wrmsr(HV_X64_MSR_REFERENCE_TSC, tsc_page_gpa | 0x1);
 GUEST_ASSERT(tsc_page->tsc_sequence != 0);

 GUEST_SYNC(5);

 check_tsc_msr_tsc_page(tsc_page);

 GUEST_SYNC(6);

 tsc_offset = tsc_page->tsc_offset;
 /* Call KVM_SET_CLOCK from userspace, check that TSC page was updated */

 GUEST_SYNC(7);
 /* Sanity check TSC page timestamp, it should be close to 0 */
 GUEST_ASSERT(get_tscpage_ts(tsc_page) < 100000);

 GUEST_ASSERT(tsc_page->tsc_offset != tsc_offset);

 nop_loop();

 /*
 * Enable Re-enlightenment and check that TSC page stays constant across
 * KVM_SET_CLOCK.
 */

 wrmsr(HV_X64_MSR_REENLIGHTENMENT_CONTROL, 0x1 << 16 | 0xff);
 wrmsr(HV_X64_MSR_TSC_EMULATION_CONTROL, 0x1);
 tsc_offset = tsc_page->tsc_offset;
 tsc_scale = tsc_page->tsc_scale;
 GUEST_SYNC(8);
 GUEST_ASSERT(tsc_page->tsc_offset == tsc_offset);
 GUEST_ASSERT(tsc_page->tsc_scale == tsc_scale);

 GUEST_SYNC(9);

 check_tsc_msr_tsc_page(tsc_page);

 /*
 * Disable re-enlightenment and TSC page, check that KVM doesn't update
 * it anymore.
 */

 wrmsr(HV_X64_MSR_REENLIGHTENMENT_CONTROL, 0);
 wrmsr(HV_X64_MSR_TSC_EMULATION_CONTROL, 0);
 wrmsr(HV_X64_MSR_REFERENCE_TSC, 0);
 memset(tsc_page, 0, sizeof(*tsc_page));

 GUEST_SYNC(10);
 GUEST_ASSERT(tsc_page->tsc_sequence == 0);
 GUEST_ASSERT(tsc_page->tsc_offset == 0);
 GUEST_ASSERT(tsc_page->tsc_scale == 0);

 GUEST_DONE();
}

static void host_check_tsc_msr_rdtsc(struct kvm_vcpu *vcpu)
{
 u64 tsc_freq, r1, r2, t1, t2;
 s64 delta_ns;

 tsc_freq = vcpu_get_msr(vcpu, HV_X64_MSR_TSC_FREQUENCY);
 TEST_ASSERT(tsc_freq > 0, "TSC frequency must be nonzero");

 /* For increased accuracy, take mean rdtsc() before and afrer ioctl */
 r1 = rdtsc();
 t1 = vcpu_get_msr(vcpu, HV_X64_MSR_TIME_REF_COUNT);
 r1 = (r1 + rdtsc()) / 2;
 nop_loop();
 r2 = rdtsc();
 t2 = vcpu_get_msr(vcpu, HV_X64_MSR_TIME_REF_COUNT);
 r2 = (r2 + rdtsc()) / 2;

 TEST_ASSERT(t2 > t1, "Time reference MSR is not monotonic (%ld <= %ld)", t1, t2);

 /* HV_X64_MSR_TIME_REF_COUNT is in 100ns */
 delta_ns = ((t2 - t1) * 100) - ((r2 - r1) * 1000000000 / tsc_freq);
 if (delta_ns < 0)
  delta_ns = -delta_ns;

 /* 1% tolerance */
 TEST_ASSERT(delta_ns * 100 < (t2 - t1) * 100,
      "Elapsed time does not match (MSR=%ld, TSC=%ld)",
      (t2 - t1) * 100, (r2 - r1) * 1000000000 / tsc_freq);
}

int main(void)
{
 struct kvm_vcpu *vcpu;
 struct kvm_vm *vm;
 struct ucall uc;
 vm_vaddr_t tsc_page_gva;
 int stage;

 TEST_REQUIRE(kvm_has_cap(KVM_CAP_HYPERV_TIME));
 TEST_REQUIRE(sys_clocksource_is_based_on_tsc());

 vm = vm_create_with_one_vcpu(&vcpu, guest_main);

 vcpu_set_hv_cpuid(vcpu);

 tsc_page_gva = vm_vaddr_alloc_page(vm);
 memset(addr_gva2hva(vm, tsc_page_gva), 0x0, getpagesize());
 TEST_ASSERT((addr_gva2gpa(vm, tsc_page_gva) & (getpagesize() - 1)) == 0,
  "TSC page has to be page aligned");
 vcpu_args_set(vcpu, 2, tsc_page_gva, addr_gva2gpa(vm, tsc_page_gva));

 host_check_tsc_msr_rdtsc(vcpu);

 for (stage = 1;; stage++) {
  vcpu_run(vcpu);
  TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);

  switch (get_ucall(vcpu, &uc)) {
  case UCALL_ABORT:
   REPORT_GUEST_ASSERT(uc);
   /* NOT REACHED */
  case UCALL_SYNC:
   break;
  case UCALL_DONE:
   /* Keep in sync with guest_main() */
   TEST_ASSERT(stage == 11, "Testing ended prematurely, stage %d",
        stage);
   goto out;
  default:
   TEST_FAIL("Unknown ucall %lu", uc.cmd);
  }

  TEST_ASSERT(!strcmp((const char *)uc.args[0], "hello") &&
       uc.args[1] == stage,
       "Stage %d: Unexpected register values vmexit, got %lx",
       stage, (ulong)uc.args[1]);

  /* Reset kvmclock triggering TSC page update */
  if (stage == 7 || stage == 8 || stage == 10) {
   struct kvm_clock_data clock = {0};

   vm_ioctl(vm, KVM_SET_CLOCK, &clock);
  }
 }

out:
 kvm_vm_free(vm);
}

Messung V0.5
C=92 H=85 G=88

¤ Dauer der Verarbeitung: 0.3 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge