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
check_preconditions(struct kvm_vm * vm)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
setup_system_counter(struct kvm_vm * vm,struct test_case * test)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
guest_read_system_counter(struct test_case * test)46 static uint64_t guest_read_system_counter(struct test_case *test)
47 {
48 return rdtsc();
49 }
50
host_read_guest_system_counter(struct test_case * test)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
guest_main(void)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
handle_sync(struct ucall * uc,uint64_t start,uint64_t end)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
handle_abort(struct ucall * uc)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
enter_guest(struct kvm_vm * vm)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
main(void)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