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 uint32_t tdcr;
23 const uint32_t 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 uint32_t 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,uint32_t val)50 static void apic_write_reg(unsigned int reg, uint32_t 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(uint64_t apic_hz,uint64_t delay_ms)58 static void apic_guest_code(uint64_t apic_hz, uint64_t delay_ms)
59 {
60 uint64_t tsc_hz = guest_tsc_khz * 1000;
61 const uint32_t tmict = ~0u;
62 uint64_t tsc0, tsc1, freq;
63 uint32_t 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(uint64_t apic_hz,uint64_t delay_ms,bool x2apic)124 static void run_apic_bus_clock_test(uint64_t apic_hz, uint64_t 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 vcpu = vm_vcpu_add(vm, 0, apic_guest_code);
141 vcpu_args_set(vcpu, 2, apic_hz, delay_ms);
142
143 ret = __vm_enable_cap(vm, KVM_CAP_X86_APIC_BUS_CYCLES_NS,
144 NSEC_PER_SEC / apic_hz);
145 TEST_ASSERT(ret < 0 && errno == EINVAL,
146 "Setting of APIC bus frequency after vCPU is created should fail.");
147
148 if (!is_x2apic)
149 virt_pg_map(vm, APIC_DEFAULT_GPA, APIC_DEFAULT_GPA);
150
151 test_apic_bus_clock(vcpu);
152 kvm_vm_free(vm);
153 }
154
help(char * name)155 static void help(char *name)
156 {
157 puts("");
158 printf("usage: %s [-h] [-d delay] [-f APIC bus freq]\n", name);
159 puts("");
160 printf("-d: Delay (in msec) guest uses to measure APIC bus frequency.\n");
161 printf("-f: The APIC bus frequency (in MHz) to be configured for the guest.\n");
162 puts("");
163 }
164
main(int argc,char * argv[])165 int main(int argc, char *argv[])
166 {
167 /*
168 * Arbitrarilty default to 25MHz for the APIC bus frequency, which is
169 * different enough from the default 1GHz to be interesting.
170 */
171 uint64_t apic_hz = 25 * 1000 * 1000;
172 uint64_t delay_ms = 100;
173 int opt;
174
175 TEST_REQUIRE(kvm_has_cap(KVM_CAP_X86_APIC_BUS_CYCLES_NS));
176
177 while ((opt = getopt(argc, argv, "d:f:h")) != -1) {
178 switch (opt) {
179 case 'f':
180 apic_hz = atoi_positive("APIC bus frequency", optarg) * 1000 * 1000;
181 break;
182 case 'd':
183 delay_ms = atoi_positive("Delay in milliseconds", optarg);
184 break;
185 case 'h':
186 default:
187 help(argv[0]);
188 exit(KSFT_SKIP);
189 }
190 }
191
192 run_apic_bus_clock_test(apic_hz, delay_ms, false);
193 run_apic_bus_clock_test(apic_hz, delay_ms, true);
194 }
195