1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Tests for MSR_IA32_TSC and MSR_IA32_TSC_ADJUST. 4 * 5 * Copyright (C) 2020, Red Hat, Inc. 6 */ 7 #include <stdio.h> 8 #include <string.h> 9 #include "kvm_util.h" 10 #include "processor.h" 11 12 #define VCPU_ID 0 13 14 #define UNITY (1ull << 30) 15 #define HOST_ADJUST (UNITY * 64) 16 #define GUEST_STEP (UNITY * 4) 17 #define ROUND(x) ((x + UNITY / 2) & -UNITY) 18 #define rounded_rdmsr(x) ROUND(rdmsr(x)) 19 #define rounded_host_rdmsr(x) ROUND(vcpu_get_msr(vm, 0, x)) 20 21 static void guest_code(void) 22 { 23 u64 val = 0; 24 25 GUEST_ASSERT_EQ(rounded_rdmsr(MSR_IA32_TSC), val); 26 GUEST_ASSERT_EQ(rounded_rdmsr(MSR_IA32_TSC_ADJUST), val); 27 28 /* Guest: writes to MSR_IA32_TSC affect both MSRs. */ 29 val = 1ull * GUEST_STEP; 30 wrmsr(MSR_IA32_TSC, val); 31 GUEST_ASSERT_EQ(rounded_rdmsr(MSR_IA32_TSC), val); 32 GUEST_ASSERT_EQ(rounded_rdmsr(MSR_IA32_TSC_ADJUST), val); 33 34 /* Guest: writes to MSR_IA32_TSC_ADJUST affect both MSRs. */ 35 GUEST_SYNC(2); 36 val = 2ull * GUEST_STEP; 37 wrmsr(MSR_IA32_TSC_ADJUST, val); 38 GUEST_ASSERT_EQ(rounded_rdmsr(MSR_IA32_TSC), val); 39 GUEST_ASSERT_EQ(rounded_rdmsr(MSR_IA32_TSC_ADJUST), val); 40 41 /* Host: setting the TSC offset. */ 42 GUEST_SYNC(3); 43 GUEST_ASSERT_EQ(rounded_rdmsr(MSR_IA32_TSC), HOST_ADJUST + val); 44 GUEST_ASSERT_EQ(rounded_rdmsr(MSR_IA32_TSC_ADJUST), val); 45 46 /* 47 * Guest: writes to MSR_IA32_TSC_ADJUST do not destroy the 48 * host-side offset and affect both MSRs. 49 */ 50 GUEST_SYNC(4); 51 val = 3ull * GUEST_STEP; 52 wrmsr(MSR_IA32_TSC_ADJUST, val); 53 GUEST_ASSERT_EQ(rounded_rdmsr(MSR_IA32_TSC), HOST_ADJUST + val); 54 GUEST_ASSERT_EQ(rounded_rdmsr(MSR_IA32_TSC_ADJUST), val); 55 56 /* 57 * Guest: writes to MSR_IA32_TSC affect both MSRs, so the host-side 58 * offset is now visible in MSR_IA32_TSC_ADJUST. 59 */ 60 GUEST_SYNC(5); 61 val = 4ull * GUEST_STEP; 62 wrmsr(MSR_IA32_TSC, val); 63 GUEST_ASSERT_EQ(rounded_rdmsr(MSR_IA32_TSC), val); 64 GUEST_ASSERT_EQ(rounded_rdmsr(MSR_IA32_TSC_ADJUST), val - HOST_ADJUST); 65 66 GUEST_DONE(); 67 } 68 69 static void run_vcpu(struct kvm_vm *vm, uint32_t vcpuid, int stage) 70 { 71 struct ucall uc; 72 73 vcpu_args_set(vm, vcpuid, 1, vcpuid); 74 75 vcpu_ioctl(vm, vcpuid, KVM_RUN, NULL); 76 77 switch (get_ucall(vm, vcpuid, &uc)) { 78 case UCALL_SYNC: 79 TEST_ASSERT(!strcmp((const char *)uc.args[0], "hello") && 80 uc.args[1] == stage + 1, "Stage %d: Unexpected register values vmexit, got %lx", 81 stage + 1, (ulong)uc.args[1]); 82 return; 83 case UCALL_DONE: 84 return; 85 case UCALL_ABORT: 86 TEST_ASSERT(false, "%s at %s:%ld\n" \ 87 "\tvalues: %#lx, %#lx", (const char *)uc.args[0], 88 __FILE__, uc.args[1], uc.args[2], uc.args[3]); 89 default: 90 TEST_ASSERT(false, "Unexpected exit: %s", 91 exit_reason_str(vcpu_state(vm, vcpuid)->exit_reason)); 92 } 93 } 94 95 int main(void) 96 { 97 struct kvm_vm *vm; 98 uint64_t val; 99 100 vm = vm_create_default(VCPU_ID, 0, guest_code); 101 102 val = 0; 103 ASSERT_EQ(rounded_host_rdmsr(MSR_IA32_TSC), val); 104 ASSERT_EQ(rounded_host_rdmsr(MSR_IA32_TSC_ADJUST), val); 105 106 /* Guest: writes to MSR_IA32_TSC affect both MSRs. */ 107 run_vcpu(vm, VCPU_ID, 1); 108 val = 1ull * GUEST_STEP; 109 ASSERT_EQ(rounded_host_rdmsr(MSR_IA32_TSC), val); 110 ASSERT_EQ(rounded_host_rdmsr(MSR_IA32_TSC_ADJUST), val); 111 112 /* Guest: writes to MSR_IA32_TSC_ADJUST affect both MSRs. */ 113 run_vcpu(vm, VCPU_ID, 2); 114 val = 2ull * GUEST_STEP; 115 ASSERT_EQ(rounded_host_rdmsr(MSR_IA32_TSC), val); 116 ASSERT_EQ(rounded_host_rdmsr(MSR_IA32_TSC_ADJUST), val); 117 118 /* 119 * Host: writes to MSR_IA32_TSC set the host-side offset 120 * and therefore do not change MSR_IA32_TSC_ADJUST. 121 */ 122 vcpu_set_msr(vm, 0, MSR_IA32_TSC, HOST_ADJUST + val); 123 ASSERT_EQ(rounded_host_rdmsr(MSR_IA32_TSC), HOST_ADJUST + val); 124 ASSERT_EQ(rounded_host_rdmsr(MSR_IA32_TSC_ADJUST), val); 125 run_vcpu(vm, VCPU_ID, 3); 126 127 /* Host: writes to MSR_IA32_TSC_ADJUST do not modify the TSC. */ 128 vcpu_set_msr(vm, 0, MSR_IA32_TSC_ADJUST, UNITY * 123456); 129 ASSERT_EQ(rounded_host_rdmsr(MSR_IA32_TSC), HOST_ADJUST + val); 130 ASSERT_EQ(vcpu_get_msr(vm, 0, MSR_IA32_TSC_ADJUST), UNITY * 123456); 131 132 /* Restore previous value. */ 133 vcpu_set_msr(vm, 0, MSR_IA32_TSC_ADJUST, val); 134 ASSERT_EQ(rounded_host_rdmsr(MSR_IA32_TSC), HOST_ADJUST + val); 135 ASSERT_EQ(rounded_host_rdmsr(MSR_IA32_TSC_ADJUST), val); 136 137 /* 138 * Guest: writes to MSR_IA32_TSC_ADJUST do not destroy the 139 * host-side offset and affect both MSRs. 140 */ 141 run_vcpu(vm, VCPU_ID, 4); 142 val = 3ull * GUEST_STEP; 143 ASSERT_EQ(rounded_host_rdmsr(MSR_IA32_TSC), HOST_ADJUST + val); 144 ASSERT_EQ(rounded_host_rdmsr(MSR_IA32_TSC_ADJUST), val); 145 146 /* 147 * Guest: writes to MSR_IA32_TSC affect both MSRs, so the host-side 148 * offset is now visible in MSR_IA32_TSC_ADJUST. 149 */ 150 run_vcpu(vm, VCPU_ID, 5); 151 val = 4ull * GUEST_STEP; 152 ASSERT_EQ(rounded_host_rdmsr(MSR_IA32_TSC), val); 153 ASSERT_EQ(rounded_host_rdmsr(MSR_IA32_TSC_ADJUST), val - HOST_ADJUST); 154 155 kvm_vm_free(vm); 156 157 return 0; 158 } 159