1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (C) 2021, Google LLC. 4 * 5 * Tests for adjusting the system counter from userspace 6 */ 7 #include <asm/kvm_para.h> 8 #include <stdint.h> 9 #include <string.h> 10 #include <sys/stat.h> 11 #include <time.h> 12 13 #include "test_util.h" 14 #include "kvm_util.h" 15 #include "processor.h" 16 17 #define VCPU_ID 0 18 19 #ifdef __x86_64__ 20 21 struct test_case { 22 uint64_t tsc_offset; 23 }; 24 25 static struct test_case test_cases[] = { 26 { 0 }, 27 { 180 * NSEC_PER_SEC }, 28 { -180 * NSEC_PER_SEC }, 29 }; 30 31 static void check_preconditions(struct kvm_vm *vm) 32 { 33 if (!_vcpu_has_device_attr(vm, VCPU_ID, KVM_VCPU_TSC_CTRL, KVM_VCPU_TSC_OFFSET)) 34 return; 35 36 print_skip("KVM_VCPU_TSC_OFFSET not supported; skipping test"); 37 exit(KSFT_SKIP); 38 } 39 40 static void setup_system_counter(struct kvm_vm *vm, struct test_case *test) 41 { 42 vcpu_access_device_attr(vm, VCPU_ID, KVM_VCPU_TSC_CTRL, 43 KVM_VCPU_TSC_OFFSET, &test->tsc_offset, true); 44 } 45 46 static uint64_t guest_read_system_counter(struct test_case *test) 47 { 48 return rdtsc(); 49 } 50 51 static uint64_t host_read_guest_system_counter(struct test_case *test) 52 { 53 return rdtsc() + test->tsc_offset; 54 } 55 56 #else /* __x86_64__ */ 57 58 #error test not implemented for this architecture! 59 60 #endif 61 62 #define GUEST_SYNC_CLOCK(__stage, __val) \ 63 GUEST_SYNC_ARGS(__stage, __val, 0, 0, 0) 64 65 static void guest_main(void) 66 { 67 int i; 68 69 for (i = 0; i < ARRAY_SIZE(test_cases); i++) { 70 struct test_case *test = &test_cases[i]; 71 72 GUEST_SYNC_CLOCK(i, guest_read_system_counter(test)); 73 } 74 } 75 76 static void handle_sync(struct ucall *uc, uint64_t start, uint64_t end) 77 { 78 uint64_t obs = uc->args[2]; 79 80 TEST_ASSERT(start <= obs && obs <= end, 81 "unexpected system counter value: %"PRIu64" expected range: [%"PRIu64", %"PRIu64"]", 82 obs, start, end); 83 84 pr_info("system counter value: %"PRIu64" expected range [%"PRIu64", %"PRIu64"]\n", 85 obs, start, end); 86 } 87 88 static void handle_abort(struct ucall *uc) 89 { 90 TEST_FAIL("%s at %s:%ld", (const char *)uc->args[0], 91 __FILE__, uc->args[1]); 92 } 93 94 static void enter_guest(struct kvm_vm *vm) 95 { 96 uint64_t start, end; 97 struct ucall uc; 98 int i; 99 100 for (i = 0; i < ARRAY_SIZE(test_cases); i++) { 101 struct test_case *test = &test_cases[i]; 102 103 setup_system_counter(vm, test); 104 start = host_read_guest_system_counter(test); 105 vcpu_run(vm, VCPU_ID); 106 end = host_read_guest_system_counter(test); 107 108 switch (get_ucall(vm, VCPU_ID, &uc)) { 109 case UCALL_SYNC: 110 handle_sync(&uc, start, end); 111 break; 112 case UCALL_ABORT: 113 handle_abort(&uc); 114 return; 115 default: 116 TEST_ASSERT(0, "unhandled ucall %ld\n", 117 get_ucall(vm, VCPU_ID, &uc)); 118 } 119 } 120 } 121 122 int main(void) 123 { 124 struct kvm_vm *vm; 125 126 vm = vm_create_default(VCPU_ID, 0, guest_main); 127 check_preconditions(vm); 128 ucall_init(vm, NULL); 129 130 enter_guest(vm); 131 kvm_vm_free(vm); 132 } 133