1  // SPDX-License-Identifier: GPL-2.0-only
2  /*
3   * Copyright (C) 2021, Google LLC.
4   *
5   * Tests for adjusting the KVM clock from userspace
6   */
7  #include <asm/kvm_para.h>
8  #include <asm/pvclock.h>
9  #include <asm/pvclock-abi.h>
10  #include <stdint.h>
11  #include <string.h>
12  #include <sys/stat.h>
13  #include <time.h>
14  
15  #include "test_util.h"
16  #include "kvm_util.h"
17  #include "processor.h"
18  
19  struct test_case {
20  	uint64_t kvmclock_base;
21  	int64_t realtime_offset;
22  };
23  
24  static struct test_case test_cases[] = {
25  	{ .kvmclock_base = 0 },
26  	{ .kvmclock_base = 180 * NSEC_PER_SEC },
27  	{ .kvmclock_base = 0, .realtime_offset = -180 * NSEC_PER_SEC },
28  	{ .kvmclock_base = 0, .realtime_offset = 180 * NSEC_PER_SEC },
29  };
30  
31  #define GUEST_SYNC_CLOCK(__stage, __val)			\
32  		GUEST_SYNC_ARGS(__stage, __val, 0, 0, 0)
33  
guest_main(vm_paddr_t pvti_pa,struct pvclock_vcpu_time_info * pvti)34  static void guest_main(vm_paddr_t pvti_pa, struct pvclock_vcpu_time_info *pvti)
35  {
36  	int i;
37  
38  	wrmsr(MSR_KVM_SYSTEM_TIME_NEW, pvti_pa | KVM_MSR_ENABLED);
39  	for (i = 0; i < ARRAY_SIZE(test_cases); i++)
40  		GUEST_SYNC_CLOCK(i, __pvclock_read_cycles(pvti, rdtsc()));
41  }
42  
43  #define EXPECTED_FLAGS (KVM_CLOCK_REALTIME | KVM_CLOCK_HOST_TSC)
44  
assert_flags(struct kvm_clock_data * data)45  static inline void assert_flags(struct kvm_clock_data *data)
46  {
47  	TEST_ASSERT((data->flags & EXPECTED_FLAGS) == EXPECTED_FLAGS,
48  		    "unexpected clock data flags: %x (want set: %x)",
49  		    data->flags, EXPECTED_FLAGS);
50  }
51  
handle_sync(struct ucall * uc,struct kvm_clock_data * start,struct kvm_clock_data * end)52  static void handle_sync(struct ucall *uc, struct kvm_clock_data *start,
53  			struct kvm_clock_data *end)
54  {
55  	uint64_t obs, exp_lo, exp_hi;
56  
57  	obs = uc->args[2];
58  	exp_lo = start->clock;
59  	exp_hi = end->clock;
60  
61  	assert_flags(start);
62  	assert_flags(end);
63  
64  	TEST_ASSERT(exp_lo <= obs && obs <= exp_hi,
65  		    "unexpected kvm-clock value: %"PRIu64" expected range: [%"PRIu64", %"PRIu64"]",
66  		    obs, exp_lo, exp_hi);
67  
68  	pr_info("kvm-clock value: %"PRIu64" expected range [%"PRIu64", %"PRIu64"]\n",
69  		obs, exp_lo, exp_hi);
70  }
71  
handle_abort(struct ucall * uc)72  static void handle_abort(struct ucall *uc)
73  {
74  	REPORT_GUEST_ASSERT(*uc);
75  }
76  
setup_clock(struct kvm_vm * vm,struct test_case * test_case)77  static void setup_clock(struct kvm_vm *vm, struct test_case *test_case)
78  {
79  	struct kvm_clock_data data;
80  
81  	memset(&data, 0, sizeof(data));
82  
83  	data.clock = test_case->kvmclock_base;
84  	if (test_case->realtime_offset) {
85  		struct timespec ts;
86  		int r;
87  
88  		data.flags |= KVM_CLOCK_REALTIME;
89  		do {
90  			r = clock_gettime(CLOCK_REALTIME, &ts);
91  			if (!r)
92  				break;
93  		} while (errno == EINTR);
94  
95  		TEST_ASSERT(!r, "clock_gettime() failed: %d\n", r);
96  
97  		data.realtime = ts.tv_sec * NSEC_PER_SEC;
98  		data.realtime += ts.tv_nsec;
99  		data.realtime += test_case->realtime_offset;
100  	}
101  
102  	vm_ioctl(vm, KVM_SET_CLOCK, &data);
103  }
104  
enter_guest(struct kvm_vcpu * vcpu)105  static void enter_guest(struct kvm_vcpu *vcpu)
106  {
107  	struct kvm_clock_data start, end;
108  	struct kvm_vm *vm = vcpu->vm;
109  	struct ucall uc;
110  	int i;
111  
112  	for (i = 0; i < ARRAY_SIZE(test_cases); i++) {
113  		setup_clock(vm, &test_cases[i]);
114  
115  		vm_ioctl(vm, KVM_GET_CLOCK, &start);
116  
117  		vcpu_run(vcpu);
118  		vm_ioctl(vm, KVM_GET_CLOCK, &end);
119  
120  		TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
121  
122  		switch (get_ucall(vcpu, &uc)) {
123  		case UCALL_SYNC:
124  			handle_sync(&uc, &start, &end);
125  			break;
126  		case UCALL_ABORT:
127  			handle_abort(&uc);
128  			return;
129  		default:
130  			TEST_ASSERT(0, "unhandled ucall: %ld\n", uc.cmd);
131  		}
132  	}
133  }
134  
135  #define CLOCKSOURCE_PATH "/sys/devices/system/clocksource/clocksource0/current_clocksource"
136  
check_clocksource(void)137  static void check_clocksource(void)
138  {
139  	char *clk_name;
140  	struct stat st;
141  	FILE *fp;
142  
143  	fp = fopen(CLOCKSOURCE_PATH, "r");
144  	if (!fp) {
145  		pr_info("failed to open clocksource file: %d; assuming TSC.\n",
146  			errno);
147  		return;
148  	}
149  
150  	if (fstat(fileno(fp), &st)) {
151  		pr_info("failed to stat clocksource file: %d; assuming TSC.\n",
152  			errno);
153  		goto out;
154  	}
155  
156  	clk_name = malloc(st.st_size);
157  	TEST_ASSERT(clk_name, "failed to allocate buffer to read file\n");
158  
159  	if (!fgets(clk_name, st.st_size, fp)) {
160  		pr_info("failed to read clocksource file: %d; assuming TSC.\n",
161  			ferror(fp));
162  		goto out;
163  	}
164  
165  	TEST_ASSERT(!strncmp(clk_name, "tsc\n", st.st_size),
166  		    "clocksource not supported: %s", clk_name);
167  out:
168  	fclose(fp);
169  }
170  
main(void)171  int main(void)
172  {
173  	struct kvm_vcpu *vcpu;
174  	vm_vaddr_t pvti_gva;
175  	vm_paddr_t pvti_gpa;
176  	struct kvm_vm *vm;
177  	int flags;
178  
179  	flags = kvm_check_cap(KVM_CAP_ADJUST_CLOCK);
180  	TEST_REQUIRE(flags & KVM_CLOCK_REALTIME);
181  
182  	check_clocksource();
183  
184  	vm = vm_create_with_one_vcpu(&vcpu, guest_main);
185  
186  	pvti_gva = vm_vaddr_alloc(vm, getpagesize(), 0x10000);
187  	pvti_gpa = addr_gva2gpa(vm, pvti_gva);
188  	vcpu_args_set(vcpu, 2, pvti_gpa, pvti_gva);
189  
190  	enter_guest(vcpu);
191  	kvm_vm_free(vm);
192  }
193