When IA32_TSC is on the VM-exit MSR-store list, the value saved is not subject to the "use TSC offsetting" VM-execution control for the current VMCS. Prior to commit e79f245ddec17 ("X86/KVM: Properly update 'tsc_offset' to represent the running guest"), kvm did not handle this situation properly. Signed-off-by: Jim Mattson <jmattson@xxxxxxxxxx> Reviewed-by: Marc Orr <marcorr@xxxxxxxxxx> Reviewed-by: Peter Shier <pshier@xxxxxxxxxx> Reviewed-by: Liran Alon <liran.alon@xxxxxxxxxx> --- x86/unittests.cfg | 6 ++++++ x86/vmx.h | 1 + x86/vmx_tests.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+) diff --git a/x86/unittests.cfg b/x86/unittests.cfg index 3b21a85..9448409 100644 --- a/x86/unittests.cfg +++ b/x86/unittests.cfg @@ -572,6 +572,12 @@ extra_params = -cpu host,+vmx -m 2560 -append vmx_pending_event_hlt_test arch = x86_64 groups = vmx +[vmx_store_tsc_test] +file = vmx.flat +extra_params = -cpu host,+vmx -m 2560 -append vmx_store_tsc_test +arch = x86_64 +groups = vmx + [vmx_db_test] file = vmx.flat extra_params = -cpu host,+vmx -m 2560 -append vmx_db_test diff --git a/x86/vmx.h b/x86/vmx.h index ba47455..8a00f73 100644 --- a/x86/vmx.h +++ b/x86/vmx.h @@ -384,6 +384,7 @@ enum Ctrl_pin { enum Ctrl0 { CPU_INTR_WINDOW = 1ul << 2, + CPU_USE_TSC_OFFSET = 1ul << 3, CPU_HLT = 1ul << 7, CPU_INVLPG = 1ul << 9, CPU_MWAIT = 1ul << 10, diff --git a/x86/vmx_tests.c b/x86/vmx_tests.c index b105b23..bcdc611 100644 --- a/x86/vmx_tests.c +++ b/x86/vmx_tests.c @@ -5031,6 +5031,50 @@ static void vmx_pending_event_hlt_test(void) vmx_pending_event_test_core(true); } +#define GUEST_TSC_OFFSET (1u << 30) + +static u64 guest_tsc; + +static void vmx_store_tsc_test_guest(void) +{ + guest_tsc = rdtsc(); +} + +/* + * This test ensures that when IA32_TSC is in the VM-exit MSR-store + * list, the value saved is not subject to the TSC offset that is + * applied to RDTSC/RDTSCP/RDMSR(IA32_TSC) in guest execution. + */ +static void vmx_store_tsc_test(void) +{ + struct vmx_msr_entry msr_entry = { .index = MSR_IA32_TSC }; + u64 low, high; + + if (!(ctrl_cpu_rev[0].clr & CPU_USE_TSC_OFFSET)) { + report_skip("'Use TSC offsetting' not supported"); + return; + } + + test_set_guest(vmx_store_tsc_test_guest); + + vmcs_set_bits(CPU_EXEC_CTRL0, CPU_USE_TSC_OFFSET); + vmcs_write(EXI_MSR_ST_CNT, 1); + vmcs_write(EXIT_MSR_ST_ADDR, virt_to_phys(&msr_entry)); + vmcs_write(TSC_OFFSET, GUEST_TSC_OFFSET); + + low = rdtsc(); + enter_guest(); + high = rdtsc(); + + report("RDTSC value in the guest (%lu) is in range [%lu, %lu]", + low + GUEST_TSC_OFFSET <= guest_tsc && + guest_tsc <= high + GUEST_TSC_OFFSET, + guest_tsc, low + GUEST_TSC_OFFSET, high + GUEST_TSC_OFFSET); + report("IA32_TSC value saved in the VM-exit MSR-store list (%lu) is in range [%lu, %lu]", + low <= msr_entry.value && msr_entry.value <= high, + msr_entry.value, low, high); +} + static void vmx_db_test_guest(void) { /* @@ -5835,6 +5879,7 @@ struct vmx_test vmx_tests[] = { TEST(vmx_db_test), TEST(vmx_pending_event_test), TEST(vmx_pending_event_hlt_test), + TEST(vmx_store_tsc_test), /* EPT access tests. */ TEST(ept_access_test_not_present), TEST(ept_access_test_read_only), -- 2.19.1.930.g4563a0d9d0-goog