1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (C) 2018, Google LLC. 4 * 5 * IA32_TSC_ADJUST test 6 * 7 * According to the SDM, "if an execution of WRMSR to the 8 * IA32_TIME_STAMP_COUNTER MSR adds (or subtracts) value X from the TSC, 9 * the logical processor also adds (or subtracts) value X from the 10 * IA32_TSC_ADJUST MSR. 11 * 12 * Note that when L1 doesn't intercept writes to IA32_TSC, a 13 * WRMSR(IA32_TSC) from L2 sets L1's TSC value, not L2's perceived TSC 14 * value. 15 * 16 * This test verifies that this unusual case is handled correctly. 17 */ 18 19 #include "test_util.h" 20 #include "kvm_util.h" 21 #include "processor.h" 22 #include "vmx.h" 23 #include "svm_util.h" 24 25 #include <string.h> 26 #include <sys/ioctl.h> 27 28 #include "kselftest.h" 29 30 #ifndef MSR_IA32_TSC_ADJUST 31 #define MSR_IA32_TSC_ADJUST 0x3b 32 #endif 33 34 #define TSC_ADJUST_VALUE (1ll << 32) 35 #define TSC_OFFSET_VALUE -(1ll << 48) 36 37 enum { 38 PORT_ABORT = 0x1000, 39 PORT_REPORT, 40 PORT_DONE, 41 }; 42 43 enum { 44 VMXON_PAGE = 0, 45 VMCS_PAGE, 46 MSR_BITMAP_PAGE, 47 48 NUM_VMX_PAGES, 49 }; 50 51 /* The virtual machine object. */ 52 static struct kvm_vm *vm; 53 54 static void check_ia32_tsc_adjust(s64 max) 55 { 56 s64 adjust; 57 58 adjust = rdmsr(MSR_IA32_TSC_ADJUST); 59 GUEST_SYNC(adjust); 60 GUEST_ASSERT(adjust <= max); 61 } 62 63 static void l2_guest_code(void) 64 { 65 u64 l1_tsc = rdtsc() - TSC_OFFSET_VALUE; 66 67 wrmsr(MSR_IA32_TSC, l1_tsc - TSC_ADJUST_VALUE); 68 check_ia32_tsc_adjust(-2 * TSC_ADJUST_VALUE); 69 70 /* Exit to L1 */ 71 __asm__ __volatile__("vmcall"); 72 } 73 74 static void l1_guest_code(void *data) 75 { 76 /* Set TSC from L1 and make sure TSC_ADJUST is updated correctly */ 77 GUEST_ASSERT(rdtsc() < TSC_ADJUST_VALUE); 78 wrmsr(MSR_IA32_TSC, rdtsc() - TSC_ADJUST_VALUE); 79 check_ia32_tsc_adjust(-1 * TSC_ADJUST_VALUE); 80 81 /* 82 * Run L2 with TSC_OFFSET. L2 will write to TSC, and L1 is not 83 * intercepting the write so it should update L1's TSC_ADJUST. 84 */ 85 if (this_cpu_has(X86_FEATURE_VMX)) { 86 struct vmx_pages *vmx_pages = data; 87 u32 control; 88 89 GUEST_ASSERT(prepare_for_vmx_operation(vmx_pages)); 90 GUEST_ASSERT(load_vmcs(vmx_pages)); 91 92 prepare_vmcs(vmx_pages, l2_guest_code); 93 control = vmreadz(CPU_BASED_VM_EXEC_CONTROL); 94 control |= CPU_BASED_USE_MSR_BITMAPS | CPU_BASED_USE_TSC_OFFSETTING; 95 vmwrite(CPU_BASED_VM_EXEC_CONTROL, control); 96 vmwrite(TSC_OFFSET, TSC_OFFSET_VALUE); 97 98 GUEST_ASSERT(!vmlaunch()); 99 GUEST_ASSERT(vmreadz(VM_EXIT_REASON) == EXIT_REASON_VMCALL); 100 } else { 101 struct svm_test_data *svm = data; 102 103 generic_svm_setup(svm, l2_guest_code); 104 105 svm->vmcb->control.tsc_offset = TSC_OFFSET_VALUE; 106 run_guest(svm->vmcb, svm->vmcb_gpa); 107 GUEST_ASSERT(svm->vmcb->control.exit_code == SVM_EXIT_VMMCALL); 108 } 109 110 check_ia32_tsc_adjust(-2 * TSC_ADJUST_VALUE); 111 GUEST_DONE(); 112 } 113 114 static void report(s64 val) 115 { 116 pr_info("IA32_TSC_ADJUST is %ld (%lld * TSC_ADJUST_VALUE + %lld).\n", 117 val, val / TSC_ADJUST_VALUE, val % TSC_ADJUST_VALUE); 118 } 119 120 int main(int argc, char *argv[]) 121 { 122 gva_t nested_gva; 123 struct kvm_vcpu *vcpu; 124 125 TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_VMX) || 126 kvm_cpu_has(X86_FEATURE_SVM)); 127 128 vm = vm_create_with_one_vcpu(&vcpu, l1_guest_code); 129 if (kvm_cpu_has(X86_FEATURE_VMX)) 130 vcpu_alloc_vmx(vm, &nested_gva); 131 else 132 vcpu_alloc_svm(vm, &nested_gva); 133 134 vcpu_args_set(vcpu, 1, nested_gva); 135 136 for (;;) { 137 struct ucall uc; 138 139 vcpu_run(vcpu); 140 TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO); 141 142 switch (get_ucall(vcpu, &uc)) { 143 case UCALL_ABORT: 144 REPORT_GUEST_ASSERT(uc); 145 /* NOT REACHED */ 146 case UCALL_SYNC: 147 report(uc.args[1]); 148 break; 149 case UCALL_DONE: 150 goto done; 151 default: 152 TEST_FAIL("Unknown ucall %lu", uc.cmd); 153 } 154 } 155 156 done: 157 kvm_vm_free(vm); 158 return 0; 159 } 160