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 } 195