xref: /linux/tools/testing/selftests/kvm/system_counter_offset_test.c (revision 79790b6818e96c58fe2bffee1b418c16e64e7b80)
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 #ifdef __x86_64__
18 
19 struct test_case {
20 	uint64_t tsc_offset;
21 };
22 
23 static struct test_case test_cases[] = {
24 	{ 0 },
25 	{ 180 * NSEC_PER_SEC },
26 	{ -180 * NSEC_PER_SEC },
27 };
28 
check_preconditions(struct kvm_vcpu * vcpu)29 static void check_preconditions(struct kvm_vcpu *vcpu)
30 {
31 	__TEST_REQUIRE(!__vcpu_has_device_attr(vcpu, KVM_VCPU_TSC_CTRL,
32 					       KVM_VCPU_TSC_OFFSET),
33 		       "KVM_VCPU_TSC_OFFSET not supported; skipping test");
34 }
35 
setup_system_counter(struct kvm_vcpu * vcpu,struct test_case * test)36 static void setup_system_counter(struct kvm_vcpu *vcpu, struct test_case *test)
37 {
38 	vcpu_device_attr_set(vcpu, KVM_VCPU_TSC_CTRL, KVM_VCPU_TSC_OFFSET,
39 			     &test->tsc_offset);
40 }
41 
guest_read_system_counter(struct test_case * test)42 static uint64_t guest_read_system_counter(struct test_case *test)
43 {
44 	return rdtsc();
45 }
46 
host_read_guest_system_counter(struct test_case * test)47 static uint64_t host_read_guest_system_counter(struct test_case *test)
48 {
49 	return rdtsc() + test->tsc_offset;
50 }
51 
52 #else /* __x86_64__ */
53 
54 #error test not implemented for this architecture!
55 
56 #endif
57 
58 #define GUEST_SYNC_CLOCK(__stage, __val)			\
59 		GUEST_SYNC_ARGS(__stage, __val, 0, 0, 0)
60 
guest_main(void)61 static void guest_main(void)
62 {
63 	int i;
64 
65 	for (i = 0; i < ARRAY_SIZE(test_cases); i++) {
66 		struct test_case *test = &test_cases[i];
67 
68 		GUEST_SYNC_CLOCK(i, guest_read_system_counter(test));
69 	}
70 }
71 
handle_sync(struct ucall * uc,uint64_t start,uint64_t end)72 static void handle_sync(struct ucall *uc, uint64_t start, uint64_t end)
73 {
74 	uint64_t obs = uc->args[2];
75 
76 	TEST_ASSERT(start <= obs && obs <= end,
77 		    "unexpected system counter value: %"PRIu64" expected range: [%"PRIu64", %"PRIu64"]",
78 		    obs, start, end);
79 
80 	pr_info("system counter value: %"PRIu64" expected range [%"PRIu64", %"PRIu64"]\n",
81 		obs, start, end);
82 }
83 
handle_abort(struct ucall * uc)84 static void handle_abort(struct ucall *uc)
85 {
86 	REPORT_GUEST_ASSERT(*uc);
87 }
88 
enter_guest(struct kvm_vcpu * vcpu)89 static void enter_guest(struct kvm_vcpu *vcpu)
90 {
91 	uint64_t start, end;
92 	struct ucall uc;
93 	int i;
94 
95 	for (i = 0; i < ARRAY_SIZE(test_cases); i++) {
96 		struct test_case *test = &test_cases[i];
97 
98 		setup_system_counter(vcpu, test);
99 		start = host_read_guest_system_counter(test);
100 		vcpu_run(vcpu);
101 		end = host_read_guest_system_counter(test);
102 
103 		switch (get_ucall(vcpu, &uc)) {
104 		case UCALL_SYNC:
105 			handle_sync(&uc, start, end);
106 			break;
107 		case UCALL_ABORT:
108 			handle_abort(&uc);
109 			return;
110 		default:
111 			TEST_ASSERT(0, "unhandled ucall %ld",
112 				    get_ucall(vcpu, &uc));
113 		}
114 	}
115 }
116 
main(void)117 int main(void)
118 {
119 	struct kvm_vcpu *vcpu;
120 	struct kvm_vm *vm;
121 
122 	vm = vm_create_with_one_vcpu(&vcpu, guest_main);
123 	check_preconditions(vcpu);
124 
125 	enter_guest(vcpu);
126 	kvm_vm_free(vm);
127 }
128