Skip to content

Commit 82222ee

Browse files
yamahatasean-jc
authored andcommitted
KVM: selftests: Add test for configure of x86 APIC bus frequency
Test if KVM emulates the APIC bus clock at the expected frequency when userspace configures the frequency via KVM_CAP_X86_APIC_BUS_CYCLES_NS. Set APIC timer's initial count to the maximum value and busy wait for 100 msec (largely arbitrary) using the TSC. Read the APIC timer's "current count" to calculate the actual APIC bus clock frequency based on TSC frequency. Suggested-by: Sean Christopherson <[email protected]> Signed-off-by: Isaku Yamahata <[email protected]> Co-developed-by: Reinette Chatre <[email protected]> Signed-off-by: Reinette Chatre <[email protected]> Link: https://lore.kernel.org/r/2fccf35715b5ba8aec5e5708d86ad7015b8d74e6.1718214999.git.reinette.chatre@intel.com Signed-off-by: Sean Christopherson <[email protected]>
1 parent 6b878cb commit 82222ee

File tree

3 files changed

+203
-0
lines changed

3 files changed

+203
-0
lines changed

tools/testing/selftests/kvm/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ TEST_GEN_PROGS_x86_64 += x86_64/vmx_invalid_nested_guest_state
112112
TEST_GEN_PROGS_x86_64 += x86_64/vmx_set_nested_state_test
113113
TEST_GEN_PROGS_x86_64 += x86_64/vmx_tsc_adjust_test
114114
TEST_GEN_PROGS_x86_64 += x86_64/vmx_nested_tsc_scaling_test
115+
TEST_GEN_PROGS_x86_64 += x86_64/apic_bus_clock_test
115116
TEST_GEN_PROGS_x86_64 += x86_64/xapic_ipi_test
116117
TEST_GEN_PROGS_x86_64 += x86_64/xapic_state_test
117118
TEST_GEN_PROGS_x86_64 += x86_64/xcr0_cpuid_test

tools/testing/selftests/kvm/include/x86_64/apic.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,14 @@
6060
#define APIC_VECTOR_MASK 0x000FF
6161
#define APIC_ICR2 0x310
6262
#define SET_APIC_DEST_FIELD(x) ((x) << 24)
63+
#define APIC_LVTT 0x320
64+
#define APIC_LVT_TIMER_ONESHOT (0 << 17)
65+
#define APIC_LVT_TIMER_PERIODIC (1 << 17)
66+
#define APIC_LVT_TIMER_TSCDEADLINE (2 << 17)
67+
#define APIC_LVT_MASKED (1 << 16)
68+
#define APIC_TMICT 0x380
69+
#define APIC_TMCCT 0x390
70+
#define APIC_TDCR 0x3E0
6371

6472
void apic_disable(void);
6573
void xapic_enable(void);
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
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+
37+
static void apic_enable(void)
38+
{
39+
if (is_x2apic)
40+
x2apic_enable();
41+
else
42+
xapic_enable();
43+
}
44+
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+
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+
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+
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+
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+
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+
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+
}

0 commit comments

Comments
 (0)