@@ -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
@@ -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;
@@ -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);
@@ -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);
@@ -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 }
};
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(+)