arch_timer.c 5.86 KB
Newer Older
1 2 3
// SPDX-License-Identifier: GPL-2.0-only
/*
 * The test validates both the virtual and physical timer IRQs using
4
 * CVAL and TVAL registers.
5 6 7 8
 *
 * Copyright (c) 2021, Google LLC.
 */
#include "arch_timer.h"
9
#include "delay.h"
10
#include "gic.h"
11 12
#include "processor.h"
#include "timer_test.h"
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
#include "vgic.h"

#define GICD_BASE_GPA			0x8000000ULL
#define GICR_BASE_GPA			0x80A0000ULL

enum guest_stage {
	GUEST_STAGE_VTIMER_CVAL = 1,
	GUEST_STAGE_VTIMER_TVAL,
	GUEST_STAGE_PTIMER_CVAL,
	GUEST_STAGE_PTIMER_TVAL,
	GUEST_STAGE_MAX,
};

static int vtimer_irq, ptimer_irq;

static void
guest_configure_timer_action(struct test_vcpu_shared_data *shared_data)
{
	switch (shared_data->guest_stage) {
	case GUEST_STAGE_VTIMER_CVAL:
		timer_set_next_cval_ms(VIRTUAL, test_args.timer_period_ms);
		shared_data->xcnt = timer_get_cntct(VIRTUAL);
		timer_set_ctl(VIRTUAL, CTL_ENABLE);
		break;
	case GUEST_STAGE_VTIMER_TVAL:
		timer_set_next_tval_ms(VIRTUAL, test_args.timer_period_ms);
		shared_data->xcnt = timer_get_cntct(VIRTUAL);
		timer_set_ctl(VIRTUAL, CTL_ENABLE);
		break;
	case GUEST_STAGE_PTIMER_CVAL:
		timer_set_next_cval_ms(PHYSICAL, test_args.timer_period_ms);
		shared_data->xcnt = timer_get_cntct(PHYSICAL);
		timer_set_ctl(PHYSICAL, CTL_ENABLE);
		break;
	case GUEST_STAGE_PTIMER_TVAL:
		timer_set_next_tval_ms(PHYSICAL, test_args.timer_period_ms);
		shared_data->xcnt = timer_get_cntct(PHYSICAL);
		timer_set_ctl(PHYSICAL, CTL_ENABLE);
		break;
	default:
		GUEST_ASSERT(0);
	}
}

static void guest_validate_irq(unsigned int intid,
				struct test_vcpu_shared_data *shared_data)
{
	enum guest_stage stage = shared_data->guest_stage;
	uint64_t xcnt = 0, xcnt_diff_us, cval = 0;
	unsigned long xctl = 0;
	unsigned int timer_irq = 0;
64
	unsigned int accessor;
65

66 67 68 69 70 71 72
	if (intid == IAR_SPURIOUS)
		return;

	switch (stage) {
	case GUEST_STAGE_VTIMER_CVAL:
	case GUEST_STAGE_VTIMER_TVAL:
		accessor = VIRTUAL;
73
		timer_irq = vtimer_irq;
74 75 76 77
		break;
	case GUEST_STAGE_PTIMER_CVAL:
	case GUEST_STAGE_PTIMER_TVAL:
		accessor = PHYSICAL;
78
		timer_irq = ptimer_irq;
79 80
		break;
	default:
81
		GUEST_ASSERT(0);
82
		return;
83 84
	}

85 86 87 88 89 90 91 92
	xctl = timer_get_ctl(accessor);
	if ((xctl & CTL_IMASK) || !(xctl & CTL_ENABLE))
		return;

	timer_set_ctl(accessor, CTL_IMASK);
	xcnt = timer_get_cntct(accessor);
	cval = timer_get_cval(accessor);

93 94 95
	xcnt_diff_us = cycles_to_usec(xcnt - shared_data->xcnt);

	/* Make sure we are dealing with the correct timer IRQ */
96
	GUEST_ASSERT_EQ(intid, timer_irq);
97 98

	/* Basic 'timer condition met' check */
99
	__GUEST_ASSERT(xcnt >= cval,
100
		       "xcnt = 0x%lx, cval = 0x%lx, xcnt_diff_us = 0x%lx",
101
		       xcnt, cval, xcnt_diff_us);
102
	__GUEST_ASSERT(xctl & CTL_ISTATUS, "xctl = 0x%lx", xctl);
103 104

	WRITE_ONCE(shared_data->nr_iter, shared_data->nr_iter + 1);
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
}

static void guest_irq_handler(struct ex_regs *regs)
{
	unsigned int intid = gic_get_and_ack_irq();
	uint32_t cpu = guest_get_vcpuid();
	struct test_vcpu_shared_data *shared_data = &vcpu_shared_data[cpu];

	guest_validate_irq(intid, shared_data);

	gic_set_eoi(intid);
}

static void guest_run_stage(struct test_vcpu_shared_data *shared_data,
				enum guest_stage stage)
{
	uint32_t irq_iter, config_iter;

	shared_data->guest_stage = stage;
	shared_data->nr_iter = 0;

	for (config_iter = 0; config_iter < test_args.nr_iter; config_iter++) {
		/* Setup the next interrupt */
		guest_configure_timer_action(shared_data);

		/* Setup a timeout for the interrupt to arrive */
		udelay(msecs_to_usecs(test_args.timer_period_ms) +
132
			test_args.timer_err_margin_us);
133 134

		irq_iter = READ_ONCE(shared_data->nr_iter);
135
		__GUEST_ASSERT(config_iter + 1 == irq_iter,
136
				"config_iter + 1 = 0x%x, irq_iter = 0x%x.\n"
137
				"  Guest timer interrupt was not triggered within the specified\n"
138 139
				"  interval, try to increase the error margin by [-e] option.\n",
				config_iter + 1, irq_iter);
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
	}
}

static void guest_code(void)
{
	uint32_t cpu = guest_get_vcpuid();
	struct test_vcpu_shared_data *shared_data = &vcpu_shared_data[cpu];

	local_irq_disable();

	gic_init(GIC_V3, test_args.nr_vcpus,
		(void *)GICD_BASE_GPA, (void *)GICR_BASE_GPA);

	timer_set_ctl(VIRTUAL, CTL_IMASK);
	timer_set_ctl(PHYSICAL, CTL_IMASK);

	gic_irq_enable(vtimer_irq);
	gic_irq_enable(ptimer_irq);
	local_irq_enable();

	guest_run_stage(shared_data, GUEST_STAGE_VTIMER_CVAL);
	guest_run_stage(shared_data, GUEST_STAGE_VTIMER_TVAL);
	guest_run_stage(shared_data, GUEST_STAGE_PTIMER_CVAL);
	guest_run_stage(shared_data, GUEST_STAGE_PTIMER_TVAL);

	GUEST_DONE();
}

static void test_init_timer_irq(struct kvm_vm *vm)
{
	/* Timer initid should be same for all the vCPUs, so query only vCPU-0 */
171
	vcpu_device_attr_get(vcpus[0], KVM_ARM_VCPU_TIMER_CTRL,
172
			     KVM_ARM_VCPU_TIMER_IRQ_PTIMER, &ptimer_irq);
173
	vcpu_device_attr_get(vcpus[0], KVM_ARM_VCPU_TIMER_CTRL,
174
			     KVM_ARM_VCPU_TIMER_IRQ_VTIMER, &vtimer_irq);
175 176 177 178 179 180 181

	sync_global_to_guest(vm, ptimer_irq);
	sync_global_to_guest(vm, vtimer_irq);

	pr_debug("ptimer_irq: %d; vtimer_irq: %d\n", ptimer_irq, vtimer_irq);
}

182 183
static int gic_fd;

184
struct kvm_vm *test_vm_create(void)
185 186 187 188 189
{
	struct kvm_vm *vm;
	unsigned int i;
	int nr_vcpus = test_args.nr_vcpus;

190
	vm = vm_create_with_vcpus(nr_vcpus, guest_code, vcpus);
191 192 193 194

	vm_init_descriptor_tables(vm);
	vm_install_exception_handler(vm, VECTOR_IRQ_CURRENT, guest_irq_handler);

195 196 197 198 199 200 201 202
	if (!test_args.reserved) {
		if (kvm_has_cap(KVM_CAP_COUNTER_OFFSET)) {
			struct kvm_arm_counter_offset offset = {
				.counter_offset = test_args.counter_offset,
				.reserved = 0,
			};
			vm_ioctl(vm, KVM_ARM_SET_COUNTER_OFFSET, &offset);
		} else
203
			TEST_FAIL("no support for global offset");
204 205
	}

206
	for (i = 0; i < nr_vcpus; i++)
207
		vcpu_init_descriptor_tables(vcpus[i]);
208 209

	test_init_timer_irq(vm);
210
	gic_fd = vgic_v3_setup(vm, nr_vcpus, 64, GICD_BASE_GPA, GICR_BASE_GPA);
211
	__TEST_REQUIRE(gic_fd >= 0, "Failed to create vgic-v3");
212 213 214 215 216 217 218

	/* Make all the test's cmdline args visible to the guest */
	sync_global_to_guest(vm, test_args);

	return vm;
}

219
void test_vm_cleanup(struct kvm_vm *vm)
220 221 222 223
{
	close(gic_fd);
	kvm_vm_free(vm);
}