195 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (c) 2024 Intel Corporation4 *5 * Verify KVM correctly emulates the APIC bus frequency when the VMM configures6 * the frequency via KVM_CAP_X86_APIC_BUS_CYCLES_NS. Start the APIC timer by7 * programming TMICT (timer initial count) to the largest value possible (so8 * that the timer will not expire during the test). Then, after an arbitrary9 * 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, and11 * 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 APIC19 * timer frequency.20 */21static 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 35static bool is_x2apic;36 37static void apic_enable(void)38{39 if (is_x2apic)40 x2apic_enable();41 else42 xapic_enable();43}44 45static uint32_t apic_read_reg(unsigned int reg)46{47 return is_x2apic ? x2apic_read_reg(reg) : xapic_read_reg(reg);48}49 50static void apic_write_reg(unsigned int reg, uint32_t val)51{52 if (is_x2apic)53 x2apic_write_reg(reg, val);54 else55 xapic_write_reg(reg, val);56}57 58static 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 the70 * 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, as85 * 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 100static 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 124static 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 155static 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 165int main(int argc, char *argv[])166{167 /*168 * Arbitrarilty default to 25MHz for the APIC bus frequency, which is169 * 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