diff mbox series

[kvm-unit-tests,8/9] svm: add test for nested tsc scaling

Message ID 20220322205613.250925-9-mlevitsk@redhat.com (mailing list archive)
State New, archived
Headers show
Series KVM unit tests for SVM options features | expand

Commit Message

Maxim Levitsky March 22, 2022, 8:56 p.m. UTC
Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
---
 lib/x86/msr.h       |  1 +
 lib/x86/processor.h |  2 ++
 x86/svm.c           |  5 ++++
 x86/svm.h           |  3 +++
 x86/svm_tests.c     | 65 +++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 76 insertions(+)
diff mbox series

Patch

diff --git a/lib/x86/msr.h b/lib/x86/msr.h
index 5001b16..fa1c0c8 100644
--- a/lib/x86/msr.h
+++ b/lib/x86/msr.h
@@ -431,6 +431,7 @@ 
 
 /* AMD-V MSRs */
 
+#define MSR_AMD64_TSC_RATIO             0xc0000104
 #define MSR_VM_CR                       0xc0010114
 #define MSR_VM_IGNNE                    0xc0010115
 #define MSR_VM_HSAVE_PA                 0xc0010117
diff --git a/lib/x86/processor.h b/lib/x86/processor.h
index b01c3d0..b3fe924 100644
--- a/lib/x86/processor.h
+++ b/lib/x86/processor.h
@@ -189,9 +189,11 @@  static inline bool is_intel(void)
 #define	X86_FEATURE_NPT			(CPUID(0x8000000A, 0, EDX, 0))
 #define	X86_FEATURE_LBRV		(CPUID(0x8000000A, 0, EDX, 1))
 #define	X86_FEATURE_NRIPS		(CPUID(0x8000000A, 0, EDX, 3))
+#define X86_FEATURE_TSCRATEMSR  (CPUID(0x8000000A, 0, EDX, 4))
 #define	X86_FEATURE_VGIF		(CPUID(0x8000000A, 0, EDX, 16))
 
 
+
 static inline bool this_cpu_has(u64 feature)
 {
 	u32 input_eax = feature >> 32;
diff --git a/x86/svm.c b/x86/svm.c
index bb58d7c..460fc59 100644
--- a/x86/svm.c
+++ b/x86/svm.c
@@ -75,6 +75,11 @@  bool lbrv_supported(void)
     return this_cpu_has(X86_FEATURE_LBRV);
 }
 
+bool tsc_scale_supported(void)
+{
+    return this_cpu_has(X86_FEATURE_TSCRATEMSR);
+}
+
 void default_prepare(struct svm_test *test)
 {
 	vmcb_ident(vmcb);
diff --git a/x86/svm.h b/x86/svm.h
index df1b1ac..d92c4f2 100644
--- a/x86/svm.h
+++ b/x86/svm.h
@@ -147,6 +147,8 @@  struct __attribute__ ((__packed__)) vmcb_control_area {
 #define SVM_VM_CR_SVM_LOCK_MASK 0x0008ULL
 #define SVM_VM_CR_SVM_DIS_MASK  0x0010ULL
 
+#define TSC_RATIO_DEFAULT   0x0100000000ULL
+
 struct __attribute__ ((__packed__)) vmcb_seg {
 	u16 selector;
 	u16 attrib;
@@ -408,6 +410,7 @@  bool smp_supported(void);
 bool default_supported(void);
 bool vgif_supported(void);
 bool lbrv_supported(void);
+bool tsc_scale_supported(void);
 void default_prepare(struct svm_test *test);
 void default_prepare_gif_clear(struct svm_test *test);
 bool default_finished(struct svm_test *test);
diff --git a/x86/svm_tests.c b/x86/svm_tests.c
index ef8b5ee..e7bd788 100644
--- a/x86/svm_tests.c
+++ b/x86/svm_tests.c
@@ -918,6 +918,70 @@  static bool tsc_adjust_check(struct svm_test *test)
     return ok && adjust <= -2 * TSC_ADJUST_VALUE;
 }
 
+
+static u64 guest_tsc_delay_value;
+/* number of bits to shift tsc right for stable result */
+#define TSC_SHIFT 24
+#define TSC_SCALE_ITERATIONS 10
+
+static void svm_tsc_scale_guest(struct svm_test *test)
+{
+    u64 start_tsc = rdtsc();
+
+    while (rdtsc() - start_tsc < guest_tsc_delay_value)
+        cpu_relax();
+}
+
+static void svm_tsc_scale_run_testcase(u64 duration,
+        double tsc_scale, u64 tsc_offset)
+{
+    u64 start_tsc, actual_duration;
+
+    guest_tsc_delay_value = (duration << TSC_SHIFT) * tsc_scale;
+
+    test_set_guest(svm_tsc_scale_guest);
+    vmcb->control.tsc_offset = tsc_offset;
+    wrmsr(MSR_AMD64_TSC_RATIO, (u64)(tsc_scale * (1ULL << 32)));
+
+    start_tsc = rdtsc();
+
+    if (svm_vmrun() != SVM_EXIT_VMMCALL)
+        report_fail("unexpected vm exit code 0x%x", vmcb->control.exit_code);
+
+    actual_duration = (rdtsc() - start_tsc) >> TSC_SHIFT;
+
+    report(duration == actual_duration, "tsc delay (expected: %lu, actual: %lu)",
+            duration, actual_duration);
+}
+
+static void svm_tsc_scale_test(void)
+{
+    int i;
+
+    if (!tsc_scale_supported()) {
+        report_skip("TSC scale not supported in the guest");
+        return;
+    }
+
+    report(rdmsr(MSR_AMD64_TSC_RATIO) == TSC_RATIO_DEFAULT,
+           "initial TSC scale ratio");
+
+    for (i = 0 ; i < TSC_SCALE_ITERATIONS; i++) {
+
+        double tsc_scale = (double)(rdrand() % 100 + 1) / 10;
+        int duration = rdrand() % 50 + 1;
+        u64 tsc_offset = rdrand();
+
+        report_info("duration=%d, tsc_scale=%d, tsc_offset=%ld",
+                    duration, (int)(tsc_scale * 100), tsc_offset);
+
+        svm_tsc_scale_run_testcase(duration, tsc_scale, tsc_offset);
+    }
+
+    svm_tsc_scale_run_testcase(50, 255, rdrand());
+    svm_tsc_scale_run_testcase(50, 0.0001, rdrand());
+}
+
 static void latency_prepare(struct svm_test *test)
 {
     default_prepare(test);
@@ -3633,5 +3697,6 @@  struct svm_test svm_tests[] = {
     TEST(svm_intr_intercept_mix_gif2),
     TEST(svm_intr_intercept_mix_nmi),
     TEST(svm_intr_intercept_mix_smi),
+    TEST(svm_tsc_scale_test),
     { NULL, NULL, NULL, NULL, NULL, NULL, NULL }
 };