File Index
Files and directories
Sign in
FILE

arch_timer.c

File preview with syntax highlighting.

linux-6.19.14/tools/testing/selftests/kvm/loongarch/arch_timer.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * The test validates periodic/one-shot constant timer IRQ using
  4 * CSR.TCFG and CSR.TVAL registers.
  5 */
  6#include "arch_timer.h"
  7#include "kvm_util.h"
  8#include "processor.h"
  9#include "timer_test.h"
 10#include "ucall_common.h"
 11
 12static void do_idle(void)
 13{
 14	unsigned int intid;
 15	unsigned long estat;
 16
 17	__asm__ __volatile__("idle 0" : : : "memory");
 18
 19	estat = csr_read(LOONGARCH_CSR_ESTAT);
 20	intid = !!(estat & BIT(INT_TI));
 21
 22	/* Make sure pending timer IRQ arrived */
 23	GUEST_ASSERT_EQ(intid, 1);
 24	csr_write(CSR_TINTCLR_TI, LOONGARCH_CSR_TINTCLR);
 25}
 26
 27static void guest_irq_handler(struct ex_regs *regs)
 28{
 29	unsigned int intid;
 30	uint32_t cpu = guest_get_vcpuid();
 31	uint64_t xcnt, val, cfg, xcnt_diff_us;
 32	struct test_vcpu_shared_data *shared_data = &vcpu_shared_data[cpu];
 33
 34	intid = !!(regs->estat & BIT(INT_TI));
 35
 36	/* Make sure we are dealing with the correct timer IRQ */
 37	GUEST_ASSERT_EQ(intid, 1);
 38
 39	cfg = timer_get_cfg();
 40	if (cfg & CSR_TCFG_PERIOD) {
 41		WRITE_ONCE(shared_data->nr_iter, shared_data->nr_iter - 1);
 42		if (shared_data->nr_iter == 0)
 43			disable_timer();
 44		csr_write(CSR_TINTCLR_TI, LOONGARCH_CSR_TINTCLR);
 45		return;
 46	}
 47
 48	/*
 49	 * On real machine, value of LOONGARCH_CSR_TVAL is BIT_ULL(48) - 1
 50	 * On virtual machine, its value counts down from BIT_ULL(48) - 1
 51	 */
 52	val = timer_get_val();
 53	xcnt = timer_get_cycles();
 54	xcnt_diff_us = cycles_to_usec(xcnt - shared_data->xcnt);
 55
 56	/* Basic 'timer condition met' check */
 57	__GUEST_ASSERT(val > cfg,
 58			"val = 0x%lx, cfg = 0x%lx, xcnt_diff_us = 0x%lx",
 59			val, cfg, xcnt_diff_us);
 60
 61	csr_write(CSR_TINTCLR_TI, LOONGARCH_CSR_TINTCLR);
 62	WRITE_ONCE(shared_data->nr_iter, shared_data->nr_iter + 1);
 63}
 64
 65static void guest_test_period_timer(uint32_t cpu)
 66{
 67	uint32_t irq_iter, config_iter;
 68	uint64_t us;
 69	struct test_vcpu_shared_data *shared_data = &vcpu_shared_data[cpu];
 70
 71	shared_data->nr_iter = test_args.nr_iter;
 72	shared_data->xcnt = timer_get_cycles();
 73	us = msecs_to_usecs(test_args.timer_period_ms) + test_args.timer_err_margin_us;
 74	timer_set_next_cmp_ms(test_args.timer_period_ms, true);
 75
 76	for (config_iter = 0; config_iter < test_args.nr_iter; config_iter++) {
 77		/* Setup a timeout for the interrupt to arrive */
 78		udelay(us);
 79	}
 80
 81	irq_iter = READ_ONCE(shared_data->nr_iter);
 82	__GUEST_ASSERT(irq_iter == 0,
 83			"irq_iter = 0x%x.\n"
 84			"  Guest period timer interrupt was not triggered within the specified\n"
 85			"  interval, try to increase the error margin by [-e] option.\n",
 86			irq_iter);
 87}
 88
 89static void guest_test_oneshot_timer(uint32_t cpu)
 90{
 91	uint32_t irq_iter, config_iter;
 92	uint64_t us;
 93	struct test_vcpu_shared_data *shared_data = &vcpu_shared_data[cpu];
 94
 95	shared_data->nr_iter = 0;
 96	shared_data->guest_stage = 0;
 97	us = msecs_to_usecs(test_args.timer_period_ms) + test_args.timer_err_margin_us;
 98	for (config_iter = 0; config_iter < test_args.nr_iter; config_iter++) {
 99		shared_data->xcnt = timer_get_cycles();
100
101		/* Setup the next interrupt */
102		timer_set_next_cmp_ms(test_args.timer_period_ms, false);
103		/* Setup a timeout for the interrupt to arrive */
104		udelay(us);
105
106		irq_iter = READ_ONCE(shared_data->nr_iter);
107		__GUEST_ASSERT(config_iter + 1 == irq_iter,
108				"config_iter + 1 = 0x%x, irq_iter = 0x%x.\n"
109				"  Guest timer interrupt was not triggered within the specified\n"
110				"  interval, try to increase the error margin by [-e] option.\n",
111				config_iter + 1, irq_iter);
112	}
113}
114
115static void guest_test_emulate_timer(uint32_t cpu)
116{
117	uint32_t config_iter;
118	uint64_t xcnt_diff_us, us;
119	struct test_vcpu_shared_data *shared_data = &vcpu_shared_data[cpu];
120
121	local_irq_disable();
122	shared_data->nr_iter = 0;
123	us = msecs_to_usecs(test_args.timer_period_ms);
124	for (config_iter = 0; config_iter < test_args.nr_iter; config_iter++) {
125		shared_data->xcnt = timer_get_cycles();
126
127		/* Setup the next interrupt */
128		timer_set_next_cmp_ms(test_args.timer_period_ms, false);
129		do_idle();
130
131		xcnt_diff_us = cycles_to_usec(timer_get_cycles() - shared_data->xcnt);
132		__GUEST_ASSERT(xcnt_diff_us >= us,
133				"xcnt_diff_us = 0x%lx, us = 0x%lx.\n",
134				xcnt_diff_us, us);
135	}
136	local_irq_enable();
137}
138
139static void guest_time_count_test(uint32_t cpu)
140{
141	uint32_t config_iter;
142	unsigned long start, end, prev, us;
143
144	/* Assuming that test case starts to run in 1 second */
145	start = timer_get_cycles();
146	us = msec_to_cycles(1000);
147	__GUEST_ASSERT(start <= us,
148			"start = 0x%lx, us = 0x%lx.\n",
149			start, us);
150
151	us = msec_to_cycles(test_args.timer_period_ms);
152	for (config_iter = 0; config_iter < test_args.nr_iter; config_iter++) {
153		start = timer_get_cycles();
154		end = start + us;
155		/* test time count growing up always */
156		while (start < end) {
157			prev = start;
158			start = timer_get_cycles();
159			__GUEST_ASSERT(prev <= start,
160					"prev = 0x%lx, start = 0x%lx.\n",
161					prev, start);
162		}
163	}
164}
165
166static void guest_code(void)
167{
168	uint32_t cpu = guest_get_vcpuid();
169
170	/* must run at first */
171	guest_time_count_test(cpu);
172
173	timer_irq_enable();
174	local_irq_enable();
175	guest_test_period_timer(cpu);
176	guest_test_oneshot_timer(cpu);
177	guest_test_emulate_timer(cpu);
178
179	GUEST_DONE();
180}
181
182struct kvm_vm *test_vm_create(void)
183{
184	struct kvm_vm *vm;
185	int nr_vcpus = test_args.nr_vcpus;
186
187	vm = vm_create_with_vcpus(nr_vcpus, guest_code, vcpus);
188	vm_init_descriptor_tables(vm);
189	vm_install_exception_handler(vm, EXCCODE_INT, guest_irq_handler);
190
191	/* Make all the test's cmdline args visible to the guest */
192	sync_global_to_guest(vm, test_args);
193
194	return vm;
195}
196
197void test_vm_cleanup(struct kvm_vm *vm)
198{
199	kvm_vm_free(vm);
200}
~/ Read-only file preview.