1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (c) 2024 Intel Corporation
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).
15 #include "test_util.h"
18 * Possible TDCR values with matching divide count. Used to modify APIC
23 const uint32_t divide_count
;
35 static bool is_x2apic
;
37 static void apic_enable(void)
45 static uint32_t apic_read_reg(unsigned int reg
)
47 return is_x2apic
? x2apic_read_reg(reg
) : xapic_read_reg(reg
);
50 static void apic_write_reg(unsigned int reg
, uint32_t val
)
53 x2apic_write_reg(reg
, val
);
55 xapic_write_reg(reg
, val
);
58 static void apic_guest_code(uint64_t apic_hz
, uint64_t delay_ms
)
60 uint64_t tsc_hz
= guest_tsc_khz
* 1000;
61 const uint32_t tmict
= ~0u;
62 uint64_t tsc0
, tsc1
, freq
;
69 * Setup one-shot timer. The vector does not matter because the
70 * interrupt should not fire.
72 apic_write_reg(APIC_LVTT
, APIC_LVT_TIMER_ONESHOT
| APIC_LVT_MASKED
);
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
);
79 udelay(delay_ms
* 1000);
80 tmcct
= apic_read_reg(APIC_TMCCT
);
84 * Stop the timer _after_ reading the current, final count, as
85 * writing the initial counter also modifies the current count.
87 apic_write_reg(APIC_TMICT
, 0);
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
);
100 static void test_apic_bus_clock(struct kvm_vcpu
*vcpu
)
108 TEST_ASSERT_KVM_EXIT_REASON(vcpu
, KVM_EXIT_IO
);
110 switch (get_ucall(vcpu
, &uc
)) {
115 REPORT_GUEST_ASSERT(uc
);
118 TEST_FAIL("Unknown ucall %lu", uc
.cmd
);
124 static void run_apic_bus_clock_test(uint64_t apic_hz
, uint64_t delay_ms
,
127 struct kvm_vcpu
*vcpu
;
135 sync_global_to_guest(vm
, is_x2apic
);
137 vm_enable_cap(vm
, KVM_CAP_X86_APIC_BUS_CYCLES_NS
,
138 NSEC_PER_SEC
/ apic_hz
);
140 vcpu
= vm_vcpu_add(vm
, 0, apic_guest_code
);
141 vcpu_args_set(vcpu
, 2, apic_hz
, delay_ms
);
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.");
149 virt_pg_map(vm
, APIC_DEFAULT_GPA
, APIC_DEFAULT_GPA
);
151 test_apic_bus_clock(vcpu
);
155 static void help(char *name
)
158 printf("usage: %s [-h] [-d delay] [-f APIC bus freq]\n", name
);
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");
165 int main(int argc
, char *argv
[])
168 * Arbitrarilty default to 25MHz for the APIC bus frequency, which is
169 * different enough from the default 1GHz to be interesting.
171 uint64_t apic_hz
= 25 * 1000 * 1000;
172 uint64_t delay_ms
= 100;
175 TEST_REQUIRE(kvm_has_cap(KVM_CAP_X86_APIC_BUS_CYCLES_NS
));
177 while ((opt
= getopt(argc
, argv
, "d:f:h")) != -1) {
180 apic_hz
= atoi_positive("APIC bus frequency", optarg
) * 1000 * 1000;
183 delay_ms
= atoi_positive("Delay in milliseconds", optarg
);
192 run_apic_bus_clock_test(apic_hz
, delay_ms
, false);
193 run_apic_bus_clock_test(apic_hz
, delay_ms
, true);