brintos

brintos / linux-shallow public Read only

0
0
Text · 4.6 KiB · f8916bb Raw
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