xref: /openbmc/linux/tools/testing/selftests/kvm/aarch64/arch_timer.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
14959d865SRaghavendra Rao Ananta // SPDX-License-Identifier: GPL-2.0-only
24959d865SRaghavendra Rao Ananta /*
34959d865SRaghavendra Rao Ananta  * arch_timer.c - Tests the aarch64 timer IRQ functionality
44959d865SRaghavendra Rao Ananta  *
54959d865SRaghavendra Rao Ananta  * The test validates both the virtual and physical timer IRQs using
64959d865SRaghavendra Rao Ananta  * CVAL and TVAL registers. This consitutes the four stages in the test.
74959d865SRaghavendra Rao Ananta  * The guest's main thread configures the timer interrupt for a stage
84959d865SRaghavendra Rao Ananta  * and waits for it to fire, with a timeout equal to the timer period.
94959d865SRaghavendra Rao Ananta  * It asserts that the timeout doesn't exceed the timer period.
104959d865SRaghavendra Rao Ananta  *
114959d865SRaghavendra Rao Ananta  * On the other hand, upon receipt of an interrupt, the guest's interrupt
124959d865SRaghavendra Rao Ananta  * handler validates the interrupt by checking if the architectural state
134959d865SRaghavendra Rao Ananta  * is in compliance with the specifications.
144959d865SRaghavendra Rao Ananta  *
154959d865SRaghavendra Rao Ananta  * The test provides command-line options to configure the timer's
164959d865SRaghavendra Rao Ananta  * period (-p), number of vCPUs (-n), and iterations per stage (-i).
1761f6fadbSRaghavendra Rao Ananta  * To stress-test the timer stack even more, an option to migrate the
1861f6fadbSRaghavendra Rao Ananta  * vCPUs across pCPUs (-m), at a particular rate, is also provided.
194959d865SRaghavendra Rao Ananta  *
204959d865SRaghavendra Rao Ananta  * Copyright (c) 2021, Google LLC.
214959d865SRaghavendra Rao Ananta  */
224959d865SRaghavendra Rao Ananta #define _GNU_SOURCE
234959d865SRaghavendra Rao Ananta 
244959d865SRaghavendra Rao Ananta #include <stdlib.h>
254959d865SRaghavendra Rao Ananta #include <pthread.h>
264959d865SRaghavendra Rao Ananta #include <linux/kvm.h>
274959d865SRaghavendra Rao Ananta #include <linux/sizes.h>
2861f6fadbSRaghavendra Rao Ananta #include <linux/bitmap.h>
2961f6fadbSRaghavendra Rao Ananta #include <sys/sysinfo.h>
304959d865SRaghavendra Rao Ananta 
314959d865SRaghavendra Rao Ananta #include "kvm_util.h"
324959d865SRaghavendra Rao Ananta #include "processor.h"
334959d865SRaghavendra Rao Ananta #include "delay.h"
344959d865SRaghavendra Rao Ananta #include "arch_timer.h"
354959d865SRaghavendra Rao Ananta #include "gic.h"
364959d865SRaghavendra Rao Ananta #include "vgic.h"
374959d865SRaghavendra Rao Ananta 
384959d865SRaghavendra Rao Ananta #define NR_VCPUS_DEF			4
394959d865SRaghavendra Rao Ananta #define NR_TEST_ITERS_DEF		5
404959d865SRaghavendra Rao Ananta #define TIMER_TEST_PERIOD_MS_DEF	10
414959d865SRaghavendra Rao Ananta #define TIMER_TEST_ERR_MARGIN_US	100
4261f6fadbSRaghavendra Rao Ananta #define TIMER_TEST_MIGRATION_FREQ_MS	2
434959d865SRaghavendra Rao Ananta 
444959d865SRaghavendra Rao Ananta struct test_args {
454959d865SRaghavendra Rao Ananta 	int nr_vcpus;
464959d865SRaghavendra Rao Ananta 	int nr_iter;
474959d865SRaghavendra Rao Ananta 	int timer_period_ms;
4861f6fadbSRaghavendra Rao Ananta 	int migration_freq_ms;
492fe9e0fcSMarc Zyngier 	struct kvm_arm_counter_offset offset;
504959d865SRaghavendra Rao Ananta };
514959d865SRaghavendra Rao Ananta 
524959d865SRaghavendra Rao Ananta static struct test_args test_args = {
534959d865SRaghavendra Rao Ananta 	.nr_vcpus = NR_VCPUS_DEF,
544959d865SRaghavendra Rao Ananta 	.nr_iter = NR_TEST_ITERS_DEF,
554959d865SRaghavendra Rao Ananta 	.timer_period_ms = TIMER_TEST_PERIOD_MS_DEF,
5661f6fadbSRaghavendra Rao Ananta 	.migration_freq_ms = TIMER_TEST_MIGRATION_FREQ_MS,
572fe9e0fcSMarc Zyngier 	.offset = { .reserved = 1 },
584959d865SRaghavendra Rao Ananta };
594959d865SRaghavendra Rao Ananta 
604959d865SRaghavendra Rao Ananta #define msecs_to_usecs(msec)		((msec) * 1000LL)
614959d865SRaghavendra Rao Ananta 
624959d865SRaghavendra Rao Ananta #define GICD_BASE_GPA			0x8000000ULL
634959d865SRaghavendra Rao Ananta #define GICR_BASE_GPA			0x80A0000ULL
644959d865SRaghavendra Rao Ananta 
654959d865SRaghavendra Rao Ananta enum guest_stage {
664959d865SRaghavendra Rao Ananta 	GUEST_STAGE_VTIMER_CVAL = 1,
674959d865SRaghavendra Rao Ananta 	GUEST_STAGE_VTIMER_TVAL,
684959d865SRaghavendra Rao Ananta 	GUEST_STAGE_PTIMER_CVAL,
694959d865SRaghavendra Rao Ananta 	GUEST_STAGE_PTIMER_TVAL,
704959d865SRaghavendra Rao Ananta 	GUEST_STAGE_MAX,
714959d865SRaghavendra Rao Ananta };
724959d865SRaghavendra Rao Ananta 
734959d865SRaghavendra Rao Ananta /* Shared variables between host and guest */
744959d865SRaghavendra Rao Ananta struct test_vcpu_shared_data {
754959d865SRaghavendra Rao Ananta 	int nr_iter;
764959d865SRaghavendra Rao Ananta 	enum guest_stage guest_stage;
774959d865SRaghavendra Rao Ananta 	uint64_t xcnt;
784959d865SRaghavendra Rao Ananta };
794959d865SRaghavendra Rao Ananta 
807a5e4ae3SSean Christopherson static struct kvm_vcpu *vcpus[KVM_MAX_VCPUS];
817a5e4ae3SSean Christopherson static pthread_t pt_vcpu_run[KVM_MAX_VCPUS];
824959d865SRaghavendra Rao Ananta static struct test_vcpu_shared_data vcpu_shared_data[KVM_MAX_VCPUS];
834959d865SRaghavendra Rao Ananta 
844959d865SRaghavendra Rao Ananta static int vtimer_irq, ptimer_irq;
854959d865SRaghavendra Rao Ananta 
8661f6fadbSRaghavendra Rao Ananta static unsigned long *vcpu_done_map;
8761f6fadbSRaghavendra Rao Ananta static pthread_mutex_t vcpu_done_map_lock;
8861f6fadbSRaghavendra Rao Ananta 
894959d865SRaghavendra Rao Ananta static void
guest_configure_timer_action(struct test_vcpu_shared_data * shared_data)904959d865SRaghavendra Rao Ananta guest_configure_timer_action(struct test_vcpu_shared_data *shared_data)
914959d865SRaghavendra Rao Ananta {
924959d865SRaghavendra Rao Ananta 	switch (shared_data->guest_stage) {
934959d865SRaghavendra Rao Ananta 	case GUEST_STAGE_VTIMER_CVAL:
944959d865SRaghavendra Rao Ananta 		timer_set_next_cval_ms(VIRTUAL, test_args.timer_period_ms);
954959d865SRaghavendra Rao Ananta 		shared_data->xcnt = timer_get_cntct(VIRTUAL);
964959d865SRaghavendra Rao Ananta 		timer_set_ctl(VIRTUAL, CTL_ENABLE);
974959d865SRaghavendra Rao Ananta 		break;
984959d865SRaghavendra Rao Ananta 	case GUEST_STAGE_VTIMER_TVAL:
994959d865SRaghavendra Rao Ananta 		timer_set_next_tval_ms(VIRTUAL, test_args.timer_period_ms);
1004959d865SRaghavendra Rao Ananta 		shared_data->xcnt = timer_get_cntct(VIRTUAL);
1014959d865SRaghavendra Rao Ananta 		timer_set_ctl(VIRTUAL, CTL_ENABLE);
1024959d865SRaghavendra Rao Ananta 		break;
1034959d865SRaghavendra Rao Ananta 	case GUEST_STAGE_PTIMER_CVAL:
1044959d865SRaghavendra Rao Ananta 		timer_set_next_cval_ms(PHYSICAL, test_args.timer_period_ms);
1054959d865SRaghavendra Rao Ananta 		shared_data->xcnt = timer_get_cntct(PHYSICAL);
1064959d865SRaghavendra Rao Ananta 		timer_set_ctl(PHYSICAL, CTL_ENABLE);
1074959d865SRaghavendra Rao Ananta 		break;
1084959d865SRaghavendra Rao Ananta 	case GUEST_STAGE_PTIMER_TVAL:
1094959d865SRaghavendra Rao Ananta 		timer_set_next_tval_ms(PHYSICAL, test_args.timer_period_ms);
1104959d865SRaghavendra Rao Ananta 		shared_data->xcnt = timer_get_cntct(PHYSICAL);
1114959d865SRaghavendra Rao Ananta 		timer_set_ctl(PHYSICAL, CTL_ENABLE);
1124959d865SRaghavendra Rao Ananta 		break;
1134959d865SRaghavendra Rao Ananta 	default:
1144959d865SRaghavendra Rao Ananta 		GUEST_ASSERT(0);
1154959d865SRaghavendra Rao Ananta 	}
1164959d865SRaghavendra Rao Ananta }
1174959d865SRaghavendra Rao Ananta 
guest_validate_irq(unsigned int intid,struct test_vcpu_shared_data * shared_data)1184959d865SRaghavendra Rao Ananta static void guest_validate_irq(unsigned int intid,
1194959d865SRaghavendra Rao Ananta 				struct test_vcpu_shared_data *shared_data)
1204959d865SRaghavendra Rao Ananta {
1214959d865SRaghavendra Rao Ananta 	enum guest_stage stage = shared_data->guest_stage;
1224959d865SRaghavendra Rao Ananta 	uint64_t xcnt = 0, xcnt_diff_us, cval = 0;
1234959d865SRaghavendra Rao Ananta 	unsigned long xctl = 0;
1244959d865SRaghavendra Rao Ananta 	unsigned int timer_irq = 0;
125056c1566SMarc Zyngier 	unsigned int accessor;
1264959d865SRaghavendra Rao Ananta 
127056c1566SMarc Zyngier 	if (intid == IAR_SPURIOUS)
128056c1566SMarc Zyngier 		return;
129056c1566SMarc Zyngier 
130056c1566SMarc Zyngier 	switch (stage) {
131056c1566SMarc Zyngier 	case GUEST_STAGE_VTIMER_CVAL:
132056c1566SMarc Zyngier 	case GUEST_STAGE_VTIMER_TVAL:
133056c1566SMarc Zyngier 		accessor = VIRTUAL;
1344959d865SRaghavendra Rao Ananta 		timer_irq = vtimer_irq;
135056c1566SMarc Zyngier 		break;
136056c1566SMarc Zyngier 	case GUEST_STAGE_PTIMER_CVAL:
137056c1566SMarc Zyngier 	case GUEST_STAGE_PTIMER_TVAL:
138056c1566SMarc Zyngier 		accessor = PHYSICAL;
1394959d865SRaghavendra Rao Ananta 		timer_irq = ptimer_irq;
140056c1566SMarc Zyngier 		break;
141056c1566SMarc Zyngier 	default:
1424959d865SRaghavendra Rao Ananta 		GUEST_ASSERT(0);
143056c1566SMarc Zyngier 		return;
1444959d865SRaghavendra Rao Ananta 	}
1454959d865SRaghavendra Rao Ananta 
146056c1566SMarc Zyngier 	xctl = timer_get_ctl(accessor);
147056c1566SMarc Zyngier 	if ((xctl & CTL_IMASK) || !(xctl & CTL_ENABLE))
148056c1566SMarc Zyngier 		return;
149056c1566SMarc Zyngier 
150056c1566SMarc Zyngier 	timer_set_ctl(accessor, CTL_IMASK);
151056c1566SMarc Zyngier 	xcnt = timer_get_cntct(accessor);
152056c1566SMarc Zyngier 	cval = timer_get_cval(accessor);
153056c1566SMarc Zyngier 
1544959d865SRaghavendra Rao Ananta 	xcnt_diff_us = cycles_to_usec(xcnt - shared_data->xcnt);
1554959d865SRaghavendra Rao Ananta 
1564959d865SRaghavendra Rao Ananta 	/* Make sure we are dealing with the correct timer IRQ */
157*db44e1c8SSean Christopherson 	GUEST_ASSERT_EQ(intid, timer_irq);
1584959d865SRaghavendra Rao Ananta 
1594959d865SRaghavendra Rao Ananta 	/* Basic 'timer condition met' check */
160*db44e1c8SSean Christopherson 	__GUEST_ASSERT(xcnt >= cval,
161*db44e1c8SSean Christopherson 		       "xcnt = 0x%llx, cval = 0x%llx, xcnt_diff_us = 0x%llx",
162*db44e1c8SSean Christopherson 		       xcnt, cval, xcnt_diff_us);
163*db44e1c8SSean Christopherson 	__GUEST_ASSERT(xctl & CTL_ISTATUS, "xcnt = 0x%llx", xcnt);
164056c1566SMarc Zyngier 
165056c1566SMarc Zyngier 	WRITE_ONCE(shared_data->nr_iter, shared_data->nr_iter + 1);
1664959d865SRaghavendra Rao Ananta }
1674959d865SRaghavendra Rao Ananta 
guest_irq_handler(struct ex_regs * regs)1684959d865SRaghavendra Rao Ananta static void guest_irq_handler(struct ex_regs *regs)
1694959d865SRaghavendra Rao Ananta {
1704959d865SRaghavendra Rao Ananta 	unsigned int intid = gic_get_and_ack_irq();
1714959d865SRaghavendra Rao Ananta 	uint32_t cpu = guest_get_vcpuid();
1724959d865SRaghavendra Rao Ananta 	struct test_vcpu_shared_data *shared_data = &vcpu_shared_data[cpu];
1734959d865SRaghavendra Rao Ananta 
1744959d865SRaghavendra Rao Ananta 	guest_validate_irq(intid, shared_data);
1754959d865SRaghavendra Rao Ananta 
1764959d865SRaghavendra Rao Ananta 	gic_set_eoi(intid);
1774959d865SRaghavendra Rao Ananta }
1784959d865SRaghavendra Rao Ananta 
guest_run_stage(struct test_vcpu_shared_data * shared_data,enum guest_stage stage)1794959d865SRaghavendra Rao Ananta static void guest_run_stage(struct test_vcpu_shared_data *shared_data,
1804959d865SRaghavendra Rao Ananta 				enum guest_stage stage)
1814959d865SRaghavendra Rao Ananta {
1824959d865SRaghavendra Rao Ananta 	uint32_t irq_iter, config_iter;
1834959d865SRaghavendra Rao Ananta 
1844959d865SRaghavendra Rao Ananta 	shared_data->guest_stage = stage;
1854959d865SRaghavendra Rao Ananta 	shared_data->nr_iter = 0;
1864959d865SRaghavendra Rao Ananta 
1874959d865SRaghavendra Rao Ananta 	for (config_iter = 0; config_iter < test_args.nr_iter; config_iter++) {
1884959d865SRaghavendra Rao Ananta 		/* Setup the next interrupt */
1894959d865SRaghavendra Rao Ananta 		guest_configure_timer_action(shared_data);
1904959d865SRaghavendra Rao Ananta 
1914959d865SRaghavendra Rao Ananta 		/* Setup a timeout for the interrupt to arrive */
1924959d865SRaghavendra Rao Ananta 		udelay(msecs_to_usecs(test_args.timer_period_ms) +
1934959d865SRaghavendra Rao Ananta 			TIMER_TEST_ERR_MARGIN_US);
1944959d865SRaghavendra Rao Ananta 
1954959d865SRaghavendra Rao Ananta 		irq_iter = READ_ONCE(shared_data->nr_iter);
196*db44e1c8SSean Christopherson 		GUEST_ASSERT_EQ(config_iter + 1, irq_iter);
1974959d865SRaghavendra Rao Ananta 	}
1984959d865SRaghavendra Rao Ananta }
1994959d865SRaghavendra Rao Ananta 
guest_code(void)2004959d865SRaghavendra Rao Ananta static void guest_code(void)
2014959d865SRaghavendra Rao Ananta {
2024959d865SRaghavendra Rao Ananta 	uint32_t cpu = guest_get_vcpuid();
2034959d865SRaghavendra Rao Ananta 	struct test_vcpu_shared_data *shared_data = &vcpu_shared_data[cpu];
2044959d865SRaghavendra Rao Ananta 
2054959d865SRaghavendra Rao Ananta 	local_irq_disable();
2064959d865SRaghavendra Rao Ananta 
2074959d865SRaghavendra Rao Ananta 	gic_init(GIC_V3, test_args.nr_vcpus,
2084959d865SRaghavendra Rao Ananta 		(void *)GICD_BASE_GPA, (void *)GICR_BASE_GPA);
2094959d865SRaghavendra Rao Ananta 
2104959d865SRaghavendra Rao Ananta 	timer_set_ctl(VIRTUAL, CTL_IMASK);
2114959d865SRaghavendra Rao Ananta 	timer_set_ctl(PHYSICAL, CTL_IMASK);
2124959d865SRaghavendra Rao Ananta 
2134959d865SRaghavendra Rao Ananta 	gic_irq_enable(vtimer_irq);
2144959d865SRaghavendra Rao Ananta 	gic_irq_enable(ptimer_irq);
2154959d865SRaghavendra Rao Ananta 	local_irq_enable();
2164959d865SRaghavendra Rao Ananta 
2174959d865SRaghavendra Rao Ananta 	guest_run_stage(shared_data, GUEST_STAGE_VTIMER_CVAL);
2184959d865SRaghavendra Rao Ananta 	guest_run_stage(shared_data, GUEST_STAGE_VTIMER_TVAL);
2194959d865SRaghavendra Rao Ananta 	guest_run_stage(shared_data, GUEST_STAGE_PTIMER_CVAL);
2204959d865SRaghavendra Rao Ananta 	guest_run_stage(shared_data, GUEST_STAGE_PTIMER_TVAL);
2214959d865SRaghavendra Rao Ananta 
2224959d865SRaghavendra Rao Ananta 	GUEST_DONE();
2234959d865SRaghavendra Rao Ananta }
2244959d865SRaghavendra Rao Ananta 
test_vcpu_run(void * arg)2254959d865SRaghavendra Rao Ananta static void *test_vcpu_run(void *arg)
2264959d865SRaghavendra Rao Ananta {
2277a5e4ae3SSean Christopherson 	unsigned int vcpu_idx = (unsigned long)arg;
2284959d865SRaghavendra Rao Ananta 	struct ucall uc;
2297a5e4ae3SSean Christopherson 	struct kvm_vcpu *vcpu = vcpus[vcpu_idx];
2304959d865SRaghavendra Rao Ananta 	struct kvm_vm *vm = vcpu->vm;
2317a5e4ae3SSean Christopherson 	struct test_vcpu_shared_data *shared_data = &vcpu_shared_data[vcpu_idx];
2324959d865SRaghavendra Rao Ananta 
233768e9a61SSean Christopherson 	vcpu_run(vcpu);
2344959d865SRaghavendra Rao Ananta 
23561f6fadbSRaghavendra Rao Ananta 	/* Currently, any exit from guest is an indication of completion */
23661f6fadbSRaghavendra Rao Ananta 	pthread_mutex_lock(&vcpu_done_map_lock);
23703a0c819SSean Christopherson 	__set_bit(vcpu_idx, vcpu_done_map);
23861f6fadbSRaghavendra Rao Ananta 	pthread_mutex_unlock(&vcpu_done_map_lock);
23961f6fadbSRaghavendra Rao Ananta 
240768e9a61SSean Christopherson 	switch (get_ucall(vcpu, &uc)) {
2414959d865SRaghavendra Rao Ananta 	case UCALL_SYNC:
2424959d865SRaghavendra Rao Ananta 	case UCALL_DONE:
2434959d865SRaghavendra Rao Ananta 		break;
2444959d865SRaghavendra Rao Ananta 	case UCALL_ABORT:
2454959d865SRaghavendra Rao Ananta 		sync_global_from_guest(vm, *shared_data);
246*db44e1c8SSean Christopherson 		fprintf(stderr, "Guest assert failed,  vcpu %u; stage; %u; iter: %u\n",
247*db44e1c8SSean Christopherson 			vcpu_idx, shared_data->guest_stage, shared_data->nr_iter);
248*db44e1c8SSean Christopherson 		REPORT_GUEST_ASSERT(uc);
2494959d865SRaghavendra Rao Ananta 		break;
2504959d865SRaghavendra Rao Ananta 	default:
2514959d865SRaghavendra Rao Ananta 		TEST_FAIL("Unexpected guest exit\n");
2524959d865SRaghavendra Rao Ananta 	}
2534959d865SRaghavendra Rao Ananta 
2544959d865SRaghavendra Rao Ananta 	return NULL;
2554959d865SRaghavendra Rao Ananta }
2564959d865SRaghavendra Rao Ananta 
test_get_pcpu(void)25761f6fadbSRaghavendra Rao Ananta static uint32_t test_get_pcpu(void)
25861f6fadbSRaghavendra Rao Ananta {
25961f6fadbSRaghavendra Rao Ananta 	uint32_t pcpu;
26061f6fadbSRaghavendra Rao Ananta 	unsigned int nproc_conf;
26161f6fadbSRaghavendra Rao Ananta 	cpu_set_t online_cpuset;
26261f6fadbSRaghavendra Rao Ananta 
26361f6fadbSRaghavendra Rao Ananta 	nproc_conf = get_nprocs_conf();
26461f6fadbSRaghavendra Rao Ananta 	sched_getaffinity(0, sizeof(cpu_set_t), &online_cpuset);
26561f6fadbSRaghavendra Rao Ananta 
26661f6fadbSRaghavendra Rao Ananta 	/* Randomly find an available pCPU to place a vCPU on */
26761f6fadbSRaghavendra Rao Ananta 	do {
26861f6fadbSRaghavendra Rao Ananta 		pcpu = rand() % nproc_conf;
26961f6fadbSRaghavendra Rao Ananta 	} while (!CPU_ISSET(pcpu, &online_cpuset));
27061f6fadbSRaghavendra Rao Ananta 
27161f6fadbSRaghavendra Rao Ananta 	return pcpu;
27261f6fadbSRaghavendra Rao Ananta }
27361f6fadbSRaghavendra Rao Ananta 
test_migrate_vcpu(unsigned int vcpu_idx)2747a5e4ae3SSean Christopherson static int test_migrate_vcpu(unsigned int vcpu_idx)
27561f6fadbSRaghavendra Rao Ananta {
27661f6fadbSRaghavendra Rao Ananta 	int ret;
27761f6fadbSRaghavendra Rao Ananta 	cpu_set_t cpuset;
27861f6fadbSRaghavendra Rao Ananta 	uint32_t new_pcpu = test_get_pcpu();
27961f6fadbSRaghavendra Rao Ananta 
28061f6fadbSRaghavendra Rao Ananta 	CPU_ZERO(&cpuset);
28161f6fadbSRaghavendra Rao Ananta 	CPU_SET(new_pcpu, &cpuset);
28261f6fadbSRaghavendra Rao Ananta 
2837a5e4ae3SSean Christopherson 	pr_debug("Migrating vCPU: %u to pCPU: %u\n", vcpu_idx, new_pcpu);
28461f6fadbSRaghavendra Rao Ananta 
2857a5e4ae3SSean Christopherson 	ret = pthread_setaffinity_np(pt_vcpu_run[vcpu_idx],
28661f6fadbSRaghavendra Rao Ananta 				     sizeof(cpuset), &cpuset);
28761f6fadbSRaghavendra Rao Ananta 
28861f6fadbSRaghavendra Rao Ananta 	/* Allow the error where the vCPU thread is already finished */
28961f6fadbSRaghavendra Rao Ananta 	TEST_ASSERT(ret == 0 || ret == ESRCH,
29061f6fadbSRaghavendra Rao Ananta 		    "Failed to migrate the vCPU:%u to pCPU: %u; ret: %d\n",
2917a5e4ae3SSean Christopherson 		    vcpu_idx, new_pcpu, ret);
29261f6fadbSRaghavendra Rao Ananta 
29361f6fadbSRaghavendra Rao Ananta 	return ret;
29461f6fadbSRaghavendra Rao Ananta }
29561f6fadbSRaghavendra Rao Ananta 
test_vcpu_migration(void * arg)29661f6fadbSRaghavendra Rao Ananta static void *test_vcpu_migration(void *arg)
29761f6fadbSRaghavendra Rao Ananta {
29861f6fadbSRaghavendra Rao Ananta 	unsigned int i, n_done;
29961f6fadbSRaghavendra Rao Ananta 	bool vcpu_done;
30061f6fadbSRaghavendra Rao Ananta 
30161f6fadbSRaghavendra Rao Ananta 	do {
30261f6fadbSRaghavendra Rao Ananta 		usleep(msecs_to_usecs(test_args.migration_freq_ms));
30361f6fadbSRaghavendra Rao Ananta 
30461f6fadbSRaghavendra Rao Ananta 		for (n_done = 0, i = 0; i < test_args.nr_vcpus; i++) {
30561f6fadbSRaghavendra Rao Ananta 			pthread_mutex_lock(&vcpu_done_map_lock);
30661f6fadbSRaghavendra Rao Ananta 			vcpu_done = test_bit(i, vcpu_done_map);
30761f6fadbSRaghavendra Rao Ananta 			pthread_mutex_unlock(&vcpu_done_map_lock);
30861f6fadbSRaghavendra Rao Ananta 
30961f6fadbSRaghavendra Rao Ananta 			if (vcpu_done) {
31061f6fadbSRaghavendra Rao Ananta 				n_done++;
31161f6fadbSRaghavendra Rao Ananta 				continue;
31261f6fadbSRaghavendra Rao Ananta 			}
31361f6fadbSRaghavendra Rao Ananta 
3147a5e4ae3SSean Christopherson 			test_migrate_vcpu(i);
31561f6fadbSRaghavendra Rao Ananta 		}
31661f6fadbSRaghavendra Rao Ananta 	} while (test_args.nr_vcpus != n_done);
31761f6fadbSRaghavendra Rao Ananta 
31861f6fadbSRaghavendra Rao Ananta 	return NULL;
31961f6fadbSRaghavendra Rao Ananta }
32061f6fadbSRaghavendra Rao Ananta 
test_run(struct kvm_vm * vm)3214959d865SRaghavendra Rao Ananta static void test_run(struct kvm_vm *vm)
3224959d865SRaghavendra Rao Ananta {
32361f6fadbSRaghavendra Rao Ananta 	pthread_t pt_vcpu_migration;
3247a5e4ae3SSean Christopherson 	unsigned int i;
3257a5e4ae3SSean Christopherson 	int ret;
32661f6fadbSRaghavendra Rao Ananta 
32761f6fadbSRaghavendra Rao Ananta 	pthread_mutex_init(&vcpu_done_map_lock, NULL);
32861f6fadbSRaghavendra Rao Ananta 	vcpu_done_map = bitmap_zalloc(test_args.nr_vcpus);
32961f6fadbSRaghavendra Rao Ananta 	TEST_ASSERT(vcpu_done_map, "Failed to allocate vcpu done bitmap\n");
3304959d865SRaghavendra Rao Ananta 
3317a5e4ae3SSean Christopherson 	for (i = 0; i < (unsigned long)test_args.nr_vcpus; i++) {
3327a5e4ae3SSean Christopherson 		ret = pthread_create(&pt_vcpu_run[i], NULL, test_vcpu_run,
3337a5e4ae3SSean Christopherson 				     (void *)(unsigned long)i);
3344959d865SRaghavendra Rao Ananta 		TEST_ASSERT(!ret, "Failed to create vCPU-%d pthread\n", i);
3354959d865SRaghavendra Rao Ananta 	}
3364959d865SRaghavendra Rao Ananta 
33761f6fadbSRaghavendra Rao Ananta 	/* Spawn a thread to control the vCPU migrations */
33861f6fadbSRaghavendra Rao Ananta 	if (test_args.migration_freq_ms) {
33961f6fadbSRaghavendra Rao Ananta 		srand(time(NULL));
34061f6fadbSRaghavendra Rao Ananta 
34161f6fadbSRaghavendra Rao Ananta 		ret = pthread_create(&pt_vcpu_migration, NULL,
34261f6fadbSRaghavendra Rao Ananta 					test_vcpu_migration, NULL);
34361f6fadbSRaghavendra Rao Ananta 		TEST_ASSERT(!ret, "Failed to create the migration pthread\n");
34461f6fadbSRaghavendra Rao Ananta 	}
34561f6fadbSRaghavendra Rao Ananta 
34661f6fadbSRaghavendra Rao Ananta 
3474959d865SRaghavendra Rao Ananta 	for (i = 0; i < test_args.nr_vcpus; i++)
3487a5e4ae3SSean Christopherson 		pthread_join(pt_vcpu_run[i], NULL);
34961f6fadbSRaghavendra Rao Ananta 
35061f6fadbSRaghavendra Rao Ananta 	if (test_args.migration_freq_ms)
35161f6fadbSRaghavendra Rao Ananta 		pthread_join(pt_vcpu_migration, NULL);
35261f6fadbSRaghavendra Rao Ananta 
35361f6fadbSRaghavendra Rao Ananta 	bitmap_free(vcpu_done_map);
3544959d865SRaghavendra Rao Ananta }
3554959d865SRaghavendra Rao Ananta 
test_init_timer_irq(struct kvm_vm * vm)3564959d865SRaghavendra Rao Ananta static void test_init_timer_irq(struct kvm_vm *vm)
3574959d865SRaghavendra Rao Ananta {
3584959d865SRaghavendra Rao Ananta 	/* Timer initid should be same for all the vCPUs, so query only vCPU-0 */
359768e9a61SSean Christopherson 	vcpu_device_attr_get(vcpus[0], KVM_ARM_VCPU_TIMER_CTRL,
36040918184SSean Christopherson 			     KVM_ARM_VCPU_TIMER_IRQ_PTIMER, &ptimer_irq);
361768e9a61SSean Christopherson 	vcpu_device_attr_get(vcpus[0], KVM_ARM_VCPU_TIMER_CTRL,
36240918184SSean Christopherson 			     KVM_ARM_VCPU_TIMER_IRQ_VTIMER, &vtimer_irq);
3634959d865SRaghavendra Rao Ananta 
3644959d865SRaghavendra Rao Ananta 	sync_global_to_guest(vm, ptimer_irq);
3654959d865SRaghavendra Rao Ananta 	sync_global_to_guest(vm, vtimer_irq);
3664959d865SRaghavendra Rao Ananta 
3674959d865SRaghavendra Rao Ananta 	pr_debug("ptimer_irq: %d; vtimer_irq: %d\n", ptimer_irq, vtimer_irq);
3684959d865SRaghavendra Rao Ananta }
3694959d865SRaghavendra Rao Ananta 
37021db8384SOliver Upton static int gic_fd;
37121db8384SOliver Upton 
test_vm_create(void)3724959d865SRaghavendra Rao Ananta static struct kvm_vm *test_vm_create(void)
3734959d865SRaghavendra Rao Ananta {
3744959d865SRaghavendra Rao Ananta 	struct kvm_vm *vm;
3754959d865SRaghavendra Rao Ananta 	unsigned int i;
3764959d865SRaghavendra Rao Ananta 	int nr_vcpus = test_args.nr_vcpus;
3774959d865SRaghavendra Rao Ananta 
3787a5e4ae3SSean Christopherson 	vm = vm_create_with_vcpus(nr_vcpus, guest_code, vcpus);
3794959d865SRaghavendra Rao Ananta 
3804959d865SRaghavendra Rao Ananta 	vm_init_descriptor_tables(vm);
3814959d865SRaghavendra Rao Ananta 	vm_install_exception_handler(vm, VECTOR_IRQ_CURRENT, guest_irq_handler);
3824959d865SRaghavendra Rao Ananta 
3832fe9e0fcSMarc Zyngier 	if (!test_args.offset.reserved) {
3842fe9e0fcSMarc Zyngier 		if (kvm_has_cap(KVM_CAP_COUNTER_OFFSET))
3852fe9e0fcSMarc Zyngier 			vm_ioctl(vm, KVM_ARM_SET_COUNTER_OFFSET, &test_args.offset);
3862fe9e0fcSMarc Zyngier 		else
3872fe9e0fcSMarc Zyngier 			TEST_FAIL("no support for global offset\n");
3882fe9e0fcSMarc Zyngier 	}
3892fe9e0fcSMarc Zyngier 
3907a5e4ae3SSean Christopherson 	for (i = 0; i < nr_vcpus; i++)
391768e9a61SSean Christopherson 		vcpu_init_descriptor_tables(vcpus[i]);
3924959d865SRaghavendra Rao Ananta 
3934959d865SRaghavendra Rao Ananta 	test_init_timer_irq(vm);
39421db8384SOliver Upton 	gic_fd = vgic_v3_setup(vm, nr_vcpus, 64, GICD_BASE_GPA, GICR_BASE_GPA);
3957ed397d1SSean Christopherson 	__TEST_REQUIRE(gic_fd >= 0, "Failed to create vgic-v3");
3964959d865SRaghavendra Rao Ananta 
3974959d865SRaghavendra Rao Ananta 	/* Make all the test's cmdline args visible to the guest */
3984959d865SRaghavendra Rao Ananta 	sync_global_to_guest(vm, test_args);
3994959d865SRaghavendra Rao Ananta 
4004959d865SRaghavendra Rao Ananta 	return vm;
4014959d865SRaghavendra Rao Ananta }
4024959d865SRaghavendra Rao Ananta 
test_vm_cleanup(struct kvm_vm * vm)40321db8384SOliver Upton static void test_vm_cleanup(struct kvm_vm *vm)
40421db8384SOliver Upton {
40521db8384SOliver Upton 	close(gic_fd);
40621db8384SOliver Upton 	kvm_vm_free(vm);
40721db8384SOliver Upton }
40821db8384SOliver Upton 
test_print_help(char * name)4094959d865SRaghavendra Rao Ananta static void test_print_help(char *name)
4104959d865SRaghavendra Rao Ananta {
4114959d865SRaghavendra Rao Ananta 	pr_info("Usage: %s [-h] [-n nr_vcpus] [-i iterations] [-p timer_period_ms]\n",
4124959d865SRaghavendra Rao Ananta 		name);
4134959d865SRaghavendra Rao Ananta 	pr_info("\t-n: Number of vCPUs to configure (default: %u; max: %u)\n",
4144959d865SRaghavendra Rao Ananta 		NR_VCPUS_DEF, KVM_MAX_VCPUS);
4154959d865SRaghavendra Rao Ananta 	pr_info("\t-i: Number of iterations per stage (default: %u)\n",
4164959d865SRaghavendra Rao Ananta 		NR_TEST_ITERS_DEF);
4174959d865SRaghavendra Rao Ananta 	pr_info("\t-p: Periodicity (in ms) of the guest timer (default: %u)\n",
4184959d865SRaghavendra Rao Ananta 		TIMER_TEST_PERIOD_MS_DEF);
41961f6fadbSRaghavendra Rao Ananta 	pr_info("\t-m: Frequency (in ms) of vCPUs to migrate to different pCPU. 0 to turn off (default: %u)\n",
42061f6fadbSRaghavendra Rao Ananta 		TIMER_TEST_MIGRATION_FREQ_MS);
4212fe9e0fcSMarc Zyngier 	pr_info("\t-o: Counter offset (in counter cycles, default: 0)\n");
4224959d865SRaghavendra Rao Ananta 	pr_info("\t-h: print this help screen\n");
4234959d865SRaghavendra Rao Ananta }
4244959d865SRaghavendra Rao Ananta 
parse_args(int argc,char * argv[])4254959d865SRaghavendra Rao Ananta static bool parse_args(int argc, char *argv[])
4264959d865SRaghavendra Rao Ananta {
4274959d865SRaghavendra Rao Ananta 	int opt;
4284959d865SRaghavendra Rao Ananta 
4292fe9e0fcSMarc Zyngier 	while ((opt = getopt(argc, argv, "hn:i:p:m:o:")) != -1) {
4304959d865SRaghavendra Rao Ananta 		switch (opt) {
4314959d865SRaghavendra Rao Ananta 		case 'n':
4320001725dSVipin Sharma 			test_args.nr_vcpus = atoi_positive("Number of vCPUs", optarg);
4330001725dSVipin Sharma 			if (test_args.nr_vcpus > KVM_MAX_VCPUS) {
4344959d865SRaghavendra Rao Ananta 				pr_info("Max allowed vCPUs: %u\n",
4354959d865SRaghavendra Rao Ananta 					KVM_MAX_VCPUS);
4364959d865SRaghavendra Rao Ananta 				goto err;
4374959d865SRaghavendra Rao Ananta 			}
4384959d865SRaghavendra Rao Ananta 			break;
4394959d865SRaghavendra Rao Ananta 		case 'i':
4400001725dSVipin Sharma 			test_args.nr_iter = atoi_positive("Number of iterations", optarg);
4414959d865SRaghavendra Rao Ananta 			break;
4424959d865SRaghavendra Rao Ananta 		case 'p':
4430001725dSVipin Sharma 			test_args.timer_period_ms = atoi_positive("Periodicity", optarg);
4444959d865SRaghavendra Rao Ananta 			break;
44561f6fadbSRaghavendra Rao Ananta 		case 'm':
4460001725dSVipin Sharma 			test_args.migration_freq_ms = atoi_non_negative("Frequency", optarg);
44761f6fadbSRaghavendra Rao Ananta 			break;
4482fe9e0fcSMarc Zyngier 		case 'o':
4492fe9e0fcSMarc Zyngier 			test_args.offset.counter_offset = strtol(optarg, NULL, 0);
4502fe9e0fcSMarc Zyngier 			test_args.offset.reserved = 0;
4512fe9e0fcSMarc Zyngier 			break;
4524959d865SRaghavendra Rao Ananta 		case 'h':
4534959d865SRaghavendra Rao Ananta 		default:
4544959d865SRaghavendra Rao Ananta 			goto err;
4554959d865SRaghavendra Rao Ananta 		}
4564959d865SRaghavendra Rao Ananta 	}
4574959d865SRaghavendra Rao Ananta 
4584959d865SRaghavendra Rao Ananta 	return true;
4594959d865SRaghavendra Rao Ananta 
4604959d865SRaghavendra Rao Ananta err:
4614959d865SRaghavendra Rao Ananta 	test_print_help(argv[0]);
4624959d865SRaghavendra Rao Ananta 	return false;
4634959d865SRaghavendra Rao Ananta }
4644959d865SRaghavendra Rao Ananta 
main(int argc,char * argv[])4654959d865SRaghavendra Rao Ananta int main(int argc, char *argv[])
4664959d865SRaghavendra Rao Ananta {
4674959d865SRaghavendra Rao Ananta 	struct kvm_vm *vm;
4684959d865SRaghavendra Rao Ananta 
4694959d865SRaghavendra Rao Ananta 	if (!parse_args(argc, argv))
4704959d865SRaghavendra Rao Ananta 		exit(KSFT_SKIP);
4714959d865SRaghavendra Rao Ananta 
4727ed397d1SSean Christopherson 	__TEST_REQUIRE(!test_args.migration_freq_ms || get_nprocs() >= 2,
4737ed397d1SSean Christopherson 		       "At least two physical CPUs needed for vCPU migration");
47461f6fadbSRaghavendra Rao Ananta 
4754959d865SRaghavendra Rao Ananta 	vm = test_vm_create();
4764959d865SRaghavendra Rao Ananta 	test_run(vm);
47721db8384SOliver Upton 	test_vm_cleanup(vm);
4784959d865SRaghavendra Rao Ananta 
4794959d865SRaghavendra Rao Ananta 	return 0;
4804959d865SRaghavendra Rao Ananta }
481