File Index
Files and directories
Sign in
FILE

arch_timer.c

File preview with syntax highlighting.

linux-6.19.14/tools/testing/selftests/kvm/arm64/arch_timer.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * The test validates both the virtual and physical timer IRQs using
  4 * CVAL and TVAL registers.
  5 *
  6 * Copyright (c) 2021, Google LLC.
  7 */
  8#include "arch_timer.h"
  9#include "delay.h"
 10#include "gic.h"
 11#include "processor.h"
 12#include "timer_test.h"
 13#include "ucall_common.h"
 14#include "vgic.h"
 15
 16enum guest_stage {
 17	GUEST_STAGE_VTIMER_CVAL = 1,
 18	GUEST_STAGE_VTIMER_TVAL,
 19	GUEST_STAGE_PTIMER_CVAL,
 20	GUEST_STAGE_PTIMER_TVAL,
 21	GUEST_STAGE_MAX,
 22};
 23
 24static int vtimer_irq, ptimer_irq;
 25
 26static void
 27guest_configure_timer_action(struct test_vcpu_shared_data *shared_data)
 28{
 29	switch (shared_data->guest_stage) {
 30	case GUEST_STAGE_VTIMER_CVAL:
 31		timer_set_next_cval_ms(VIRTUAL, test_args.timer_period_ms);
 32		shared_data->xcnt = timer_get_cntct(VIRTUAL);
 33		timer_set_ctl(VIRTUAL, CTL_ENABLE);
 34		break;
 35	case GUEST_STAGE_VTIMER_TVAL:
 36		timer_set_next_tval_ms(VIRTUAL, test_args.timer_period_ms);
 37		shared_data->xcnt = timer_get_cntct(VIRTUAL);
 38		timer_set_ctl(VIRTUAL, CTL_ENABLE);
 39		break;
 40	case GUEST_STAGE_PTIMER_CVAL:
 41		timer_set_next_cval_ms(PHYSICAL, test_args.timer_period_ms);
 42		shared_data->xcnt = timer_get_cntct(PHYSICAL);
 43		timer_set_ctl(PHYSICAL, CTL_ENABLE);
 44		break;
 45	case GUEST_STAGE_PTIMER_TVAL:
 46		timer_set_next_tval_ms(PHYSICAL, test_args.timer_period_ms);
 47		shared_data->xcnt = timer_get_cntct(PHYSICAL);
 48		timer_set_ctl(PHYSICAL, CTL_ENABLE);
 49		break;
 50	default:
 51		GUEST_ASSERT(0);
 52	}
 53}
 54
 55static void guest_validate_irq(unsigned int intid,
 56				struct test_vcpu_shared_data *shared_data)
 57{
 58	enum guest_stage stage = shared_data->guest_stage;
 59	uint64_t xcnt = 0, xcnt_diff_us, cval = 0;
 60	unsigned long xctl = 0;
 61	unsigned int timer_irq = 0;
 62	unsigned int accessor;
 63
 64	if (intid == IAR_SPURIOUS)
 65		return;
 66
 67	switch (stage) {
 68	case GUEST_STAGE_VTIMER_CVAL:
 69	case GUEST_STAGE_VTIMER_TVAL:
 70		accessor = VIRTUAL;
 71		timer_irq = vtimer_irq;
 72		break;
 73	case GUEST_STAGE_PTIMER_CVAL:
 74	case GUEST_STAGE_PTIMER_TVAL:
 75		accessor = PHYSICAL;
 76		timer_irq = ptimer_irq;
 77		break;
 78	default:
 79		GUEST_ASSERT(0);
 80		return;
 81	}
 82
 83	xctl = timer_get_ctl(accessor);
 84	if ((xctl & CTL_IMASK) || !(xctl & CTL_ENABLE))
 85		return;
 86
 87	timer_set_ctl(accessor, CTL_IMASK);
 88	xcnt = timer_get_cntct(accessor);
 89	cval = timer_get_cval(accessor);
 90
 91	xcnt_diff_us = cycles_to_usec(xcnt - shared_data->xcnt);
 92
 93	/* Make sure we are dealing with the correct timer IRQ */
 94	GUEST_ASSERT_EQ(intid, timer_irq);
 95
 96	/* Basic 'timer condition met' check */
 97	__GUEST_ASSERT(xcnt >= cval,
 98		       "xcnt = 0x%lx, cval = 0x%lx, xcnt_diff_us = 0x%lx",
 99		       xcnt, cval, xcnt_diff_us);
100	__GUEST_ASSERT(xctl & CTL_ISTATUS, "xctl = 0x%lx", xctl);
101
102	WRITE_ONCE(shared_data->nr_iter, shared_data->nr_iter + 1);
103}
104
105static void guest_irq_handler(struct ex_regs *regs)
106{
107	unsigned int intid = gic_get_and_ack_irq();
108	uint32_t cpu = guest_get_vcpuid();
109	struct test_vcpu_shared_data *shared_data = &vcpu_shared_data[cpu];
110
111	guest_validate_irq(intid, shared_data);
112
113	gic_set_eoi(intid);
114}
115
116static void guest_run_stage(struct test_vcpu_shared_data *shared_data,
117				enum guest_stage stage)
118{
119	uint32_t irq_iter, config_iter;
120
121	shared_data->guest_stage = stage;
122	shared_data->nr_iter = 0;
123
124	for (config_iter = 0; config_iter < test_args.nr_iter; config_iter++) {
125		/* Setup the next interrupt */
126		guest_configure_timer_action(shared_data);
127
128		/* Setup a timeout for the interrupt to arrive */
129		udelay(msecs_to_usecs(test_args.timer_period_ms) +
130			test_args.timer_err_margin_us);
131
132		irq_iter = READ_ONCE(shared_data->nr_iter);
133		__GUEST_ASSERT(config_iter + 1 == irq_iter,
134				"config_iter + 1 = 0x%x, irq_iter = 0x%x.\n"
135				"  Guest timer interrupt was not triggered within the specified\n"
136				"  interval, try to increase the error margin by [-e] option.\n",
137				config_iter + 1, irq_iter);
138	}
139}
140
141static void guest_code(void)
142{
143	uint32_t cpu = guest_get_vcpuid();
144	struct test_vcpu_shared_data *shared_data = &vcpu_shared_data[cpu];
145
146	local_irq_disable();
147
148	gic_init(GIC_V3, test_args.nr_vcpus);
149
150	timer_set_ctl(VIRTUAL, CTL_IMASK);
151	timer_set_ctl(PHYSICAL, CTL_IMASK);
152
153	gic_irq_enable(vtimer_irq);
154	gic_irq_enable(ptimer_irq);
155	local_irq_enable();
156
157	guest_run_stage(shared_data, GUEST_STAGE_VTIMER_CVAL);
158	guest_run_stage(shared_data, GUEST_STAGE_VTIMER_TVAL);
159	guest_run_stage(shared_data, GUEST_STAGE_PTIMER_CVAL);
160	guest_run_stage(shared_data, GUEST_STAGE_PTIMER_TVAL);
161
162	GUEST_DONE();
163}
164
165static void test_init_timer_irq(struct kvm_vm *vm)
166{
167	/* Timer initid should be same for all the vCPUs, so query only vCPU-0 */
168	ptimer_irq = vcpu_get_ptimer_irq(vcpus[0]);
169	vtimer_irq = vcpu_get_vtimer_irq(vcpus[0]);
170
171	sync_global_to_guest(vm, ptimer_irq);
172	sync_global_to_guest(vm, vtimer_irq);
173
174	pr_debug("ptimer_irq: %d; vtimer_irq: %d\n", ptimer_irq, vtimer_irq);
175}
176
177struct kvm_vm *test_vm_create(void)
178{
179	struct kvm_vm *vm;
180	unsigned int i;
181	int nr_vcpus = test_args.nr_vcpus;
182
183	TEST_REQUIRE(kvm_supports_vgic_v3());
184
185	vm = vm_create_with_vcpus(nr_vcpus, guest_code, vcpus);
186
187	vm_init_descriptor_tables(vm);
188	vm_install_exception_handler(vm, VECTOR_IRQ_CURRENT, guest_irq_handler);
189
190	if (!test_args.reserved) {
191		if (kvm_has_cap(KVM_CAP_COUNTER_OFFSET)) {
192			struct kvm_arm_counter_offset offset = {
193				.counter_offset = test_args.counter_offset,
194				.reserved = 0,
195			};
196			vm_ioctl(vm, KVM_ARM_SET_COUNTER_OFFSET, &offset);
197		} else
198			TEST_FAIL("no support for global offset");
199	}
200
201	for (i = 0; i < nr_vcpus; i++)
202		vcpu_init_descriptor_tables(vcpus[i]);
203
204	test_init_timer_irq(vm);
205
206	/* Make all the test's cmdline args visible to the guest */
207	sync_global_to_guest(vm, test_args);
208
209	return vm;
210}
211
212void test_vm_cleanup(struct kvm_vm *vm)
213{
214	kvm_vm_free(vm);
215}
~/ Read-only file preview.