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