xref: /linux/tools/testing/selftests/kvm/x86/apic_bus_clock_test.c (revision 6a97c4d5262d02f04d1f41113b0d090ea51f08dd)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (c) 2024 Intel Corporation
4  *
5  * Verify KVM correctly emulates the APIC bus frequency when the VMM configures
6  * the frequency via KVM_CAP_X86_APIC_BUS_CYCLES_NS.  Start the APIC timer by
7  * programming TMICT (timer initial count) to the largest value possible (so
8  * that the timer will not expire during the test).  Then, after an arbitrary
9  * amount of time has elapsed, verify TMCCT (timer current count) is within 1%
10  * of the expected value based on the time elapsed, the APIC bus frequency, and
11  * the programmed TDCR (timer divide configuration register).
12  */
13 
14 #include "apic.h"
15 #include "test_util.h"
16 
17 /*
18  * Possible TDCR values with matching divide count. Used to modify APIC
19  * timer frequency.
20  */
21 static const struct {
22 	const u32 tdcr;
23 	const u32 divide_count;
24 } tdcrs[] = {
25 	{0x0, 2},
26 	{0x1, 4},
27 	{0x2, 8},
28 	{0x3, 16},
29 	{0x8, 32},
30 	{0x9, 64},
31 	{0xa, 128},
32 	{0xb, 1},
33 };
34 
35 static bool is_x2apic;
36 
apic_enable(void)37 static void apic_enable(void)
38 {
39 	if (is_x2apic)
40 		x2apic_enable();
41 	else
42 		xapic_enable();
43 }
44 
apic_read_reg(unsigned int reg)45 static u32 apic_read_reg(unsigned int reg)
46 {
47 	return is_x2apic ? x2apic_read_reg(reg) : xapic_read_reg(reg);
48 }
49 
apic_write_reg(unsigned int reg,u32 val)50 static void apic_write_reg(unsigned int reg, u32 val)
51 {
52 	if (is_x2apic)
53 		x2apic_write_reg(reg, val);
54 	else
55 		xapic_write_reg(reg, val);
56 }
57 
apic_guest_code(u64 apic_hz,u64 delay_ms)58 static void apic_guest_code(u64 apic_hz, u64 delay_ms)
59 {
60 	u64 tsc_hz = guest_tsc_khz * 1000;
61 	const u32 tmict = ~0u;
62 	u64 tsc0, tsc1, freq;
63 	u32 tmcct;
64 	int i;
65 
66 	apic_enable();
67 
68 	/*
69 	 * Setup one-shot timer.  The vector does not matter because the
70 	 * interrupt should not fire.
71 	 */
72 	apic_write_reg(APIC_LVTT, APIC_LVT_TIMER_ONESHOT | APIC_LVT_MASKED);
73 
74 	for (i = 0; i < ARRAY_SIZE(tdcrs); i++) {
75 		apic_write_reg(APIC_TDCR, tdcrs[i].tdcr);
76 		apic_write_reg(APIC_TMICT, tmict);
77 
78 		tsc0 = rdtsc();
79 		udelay(delay_ms * 1000);
80 		tmcct = apic_read_reg(APIC_TMCCT);
81 		tsc1 = rdtsc();
82 
83 		/*
84 		 * Stop the timer _after_ reading the current, final count, as
85 		 * writing the initial counter also modifies the current count.
86 		 */
87 		apic_write_reg(APIC_TMICT, 0);
88 
89 		freq = (tmict - tmcct) * tdcrs[i].divide_count * tsc_hz / (tsc1 - tsc0);
90 		/* Check if measured frequency is within 5% of configured frequency. */
91 		__GUEST_ASSERT(freq < apic_hz * 105 / 100 && freq > apic_hz * 95 / 100,
92 			       "Frequency = %lu (wanted %lu - %lu), bus = %lu, div = %u, tsc = %lu",
93 			       freq, apic_hz * 95 / 100, apic_hz * 105 / 100,
94 			       apic_hz, tdcrs[i].divide_count, tsc_hz);
95 	}
96 
97 	GUEST_DONE();
98 }
99 
test_apic_bus_clock(struct kvm_vcpu * vcpu)100 static void test_apic_bus_clock(struct kvm_vcpu *vcpu)
101 {
102 	bool done = false;
103 	struct ucall uc;
104 
105 	while (!done) {
106 		vcpu_run(vcpu);
107 
108 		TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
109 
110 		switch (get_ucall(vcpu, &uc)) {
111 		case UCALL_DONE:
112 			done = true;
113 			break;
114 		case UCALL_ABORT:
115 			REPORT_GUEST_ASSERT(uc);
116 			break;
117 		default:
118 			TEST_FAIL("Unknown ucall %lu", uc.cmd);
119 			break;
120 		}
121 	}
122 }
123 
run_apic_bus_clock_test(u64 apic_hz,u64 delay_ms,bool x2apic)124 static void run_apic_bus_clock_test(u64 apic_hz, u64 delay_ms,
125 				    bool x2apic)
126 {
127 	struct kvm_vcpu *vcpu;
128 	struct kvm_vm *vm;
129 	int ret;
130 
131 	is_x2apic = x2apic;
132 
133 	vm = vm_create(1);
134 
135 	sync_global_to_guest(vm, is_x2apic);
136 
137 	vm_enable_cap(vm, KVM_CAP_X86_APIC_BUS_CYCLES_NS,
138 		      NSEC_PER_SEC / apic_hz);
139 
140 	TEST_ASSERT_EQ(kvm_check_cap(KVM_CAP_X86_APIC_BUS_CYCLES_NS), 1);
141 	TEST_ASSERT_EQ(vm_check_cap(vm, KVM_CAP_X86_APIC_BUS_CYCLES_NS),
142 		       NSEC_PER_SEC / apic_hz);
143 
144 	vcpu = vm_vcpu_add(vm, 0, apic_guest_code);
145 	vcpu_args_set(vcpu, 2, apic_hz, delay_ms);
146 
147 	ret = __vm_enable_cap(vm, KVM_CAP_X86_APIC_BUS_CYCLES_NS,
148 			      NSEC_PER_SEC / apic_hz);
149 	TEST_ASSERT(ret < 0 && errno == EINVAL,
150 		    "Setting of APIC bus frequency after vCPU is created should fail.");
151 
152 	if (!is_x2apic)
153 		virt_pg_map(vm, APIC_DEFAULT_GPA, APIC_DEFAULT_GPA);
154 
155 	test_apic_bus_clock(vcpu);
156 	kvm_vm_free(vm);
157 }
158 
help(char * name)159 static void help(char *name)
160 {
161 	puts("");
162 	printf("usage: %s [-h] [-d delay] [-f APIC bus freq]\n", name);
163 	puts("");
164 	printf("-d: Delay (in msec) guest uses to measure APIC bus frequency.\n");
165 	printf("-f: The APIC bus frequency (in MHz) to be configured for the guest.\n");
166 	puts("");
167 }
168 
main(int argc,char * argv[])169 int main(int argc, char *argv[])
170 {
171 	/*
172 	 * Arbitrarilty default to 25MHz for the APIC bus frequency, which is
173 	 * different enough from the default 1GHz to be interesting.
174 	 */
175 	u64 apic_hz = 25 * 1000 * 1000;
176 	u64 delay_ms = 100;
177 	int opt;
178 
179 	TEST_REQUIRE(kvm_has_cap(KVM_CAP_X86_APIC_BUS_CYCLES_NS));
180 
181 	while ((opt = getopt(argc, argv, "d:f:h")) != -1) {
182 		switch (opt) {
183 		case 'f':
184 			apic_hz = atoi_positive("APIC bus frequency", optarg) * 1000 * 1000;
185 			break;
186 		case 'd':
187 			delay_ms = atoi_positive("Delay in milliseconds", optarg);
188 			break;
189 		case 'h':
190 		default:
191 			help(argv[0]);
192 			exit(KSFT_SKIP);
193 		}
194 	}
195 
196 	run_apic_bus_clock_test(apic_hz, delay_ms, false);
197 	run_apic_bus_clock_test(apic_hz, delay_ms, true);
198 }
199