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