Path: blob/master/tools/testing/selftests/kvm/arm64/arch_timer.c
38237 views
// SPDX-License-Identifier: GPL-2.0-only1/*2* The test validates both the virtual and physical timer IRQs using3* CVAL and TVAL registers.4*5* Copyright (c) 2021, Google LLC.6*/7#include "arch_timer.h"8#include "delay.h"9#include "gic.h"10#include "processor.h"11#include "timer_test.h"12#include "ucall_common.h"13#include "vgic.h"1415enum guest_stage {16GUEST_STAGE_VTIMER_CVAL = 1,17GUEST_STAGE_VTIMER_TVAL,18GUEST_STAGE_PTIMER_CVAL,19GUEST_STAGE_PTIMER_TVAL,20GUEST_STAGE_MAX,21};2223static int vtimer_irq, ptimer_irq;2425static void26guest_configure_timer_action(struct test_vcpu_shared_data *shared_data)27{28switch (shared_data->guest_stage) {29case GUEST_STAGE_VTIMER_CVAL:30timer_set_next_cval_ms(VIRTUAL, test_args.timer_period_ms);31shared_data->xcnt = timer_get_cntct(VIRTUAL);32timer_set_ctl(VIRTUAL, CTL_ENABLE);33break;34case GUEST_STAGE_VTIMER_TVAL:35timer_set_next_tval_ms(VIRTUAL, test_args.timer_period_ms);36shared_data->xcnt = timer_get_cntct(VIRTUAL);37timer_set_ctl(VIRTUAL, CTL_ENABLE);38break;39case GUEST_STAGE_PTIMER_CVAL:40timer_set_next_cval_ms(PHYSICAL, test_args.timer_period_ms);41shared_data->xcnt = timer_get_cntct(PHYSICAL);42timer_set_ctl(PHYSICAL, CTL_ENABLE);43break;44case GUEST_STAGE_PTIMER_TVAL:45timer_set_next_tval_ms(PHYSICAL, test_args.timer_period_ms);46shared_data->xcnt = timer_get_cntct(PHYSICAL);47timer_set_ctl(PHYSICAL, CTL_ENABLE);48break;49default:50GUEST_ASSERT(0);51}52}5354static void guest_validate_irq(unsigned int intid,55struct test_vcpu_shared_data *shared_data)56{57enum guest_stage stage = shared_data->guest_stage;58uint64_t xcnt = 0, xcnt_diff_us, cval = 0;59unsigned long xctl = 0;60unsigned int timer_irq = 0;61unsigned int accessor;6263if (intid == IAR_SPURIOUS)64return;6566switch (stage) {67case GUEST_STAGE_VTIMER_CVAL:68case GUEST_STAGE_VTIMER_TVAL:69accessor = VIRTUAL;70timer_irq = vtimer_irq;71break;72case GUEST_STAGE_PTIMER_CVAL:73case GUEST_STAGE_PTIMER_TVAL:74accessor = PHYSICAL;75timer_irq = ptimer_irq;76break;77default:78GUEST_ASSERT(0);79return;80}8182xctl = timer_get_ctl(accessor);83if ((xctl & CTL_IMASK) || !(xctl & CTL_ENABLE))84return;8586timer_set_ctl(accessor, CTL_IMASK);87xcnt = timer_get_cntct(accessor);88cval = timer_get_cval(accessor);8990xcnt_diff_us = cycles_to_usec(xcnt - shared_data->xcnt);9192/* Make sure we are dealing with the correct timer IRQ */93GUEST_ASSERT_EQ(intid, timer_irq);9495/* Basic 'timer condition met' check */96__GUEST_ASSERT(xcnt >= cval,97"xcnt = 0x%lx, cval = 0x%lx, xcnt_diff_us = 0x%lx",98xcnt, cval, xcnt_diff_us);99__GUEST_ASSERT(xctl & CTL_ISTATUS, "xctl = 0x%lx", xctl);100101WRITE_ONCE(shared_data->nr_iter, shared_data->nr_iter + 1);102}103104static void guest_irq_handler(struct ex_regs *regs)105{106unsigned int intid = gic_get_and_ack_irq();107uint32_t cpu = guest_get_vcpuid();108struct test_vcpu_shared_data *shared_data = &vcpu_shared_data[cpu];109110guest_validate_irq(intid, shared_data);111112gic_set_eoi(intid);113}114115static void guest_run_stage(struct test_vcpu_shared_data *shared_data,116enum guest_stage stage)117{118uint32_t irq_iter, config_iter;119120shared_data->guest_stage = stage;121shared_data->nr_iter = 0;122123for (config_iter = 0; config_iter < test_args.nr_iter; config_iter++) {124/* Setup the next interrupt */125guest_configure_timer_action(shared_data);126127/* Setup a timeout for the interrupt to arrive */128udelay(msecs_to_usecs(test_args.timer_period_ms) +129test_args.timer_err_margin_us);130131irq_iter = READ_ONCE(shared_data->nr_iter);132__GUEST_ASSERT(config_iter + 1 == irq_iter,133"config_iter + 1 = 0x%x, irq_iter = 0x%x.\n"134" Guest timer interrupt was not triggered within the specified\n"135" interval, try to increase the error margin by [-e] option.\n",136config_iter + 1, irq_iter);137}138}139140static void guest_code(void)141{142uint32_t cpu = guest_get_vcpuid();143struct test_vcpu_shared_data *shared_data = &vcpu_shared_data[cpu];144145local_irq_disable();146147gic_init(GIC_V3, test_args.nr_vcpus);148149timer_set_ctl(VIRTUAL, CTL_IMASK);150timer_set_ctl(PHYSICAL, CTL_IMASK);151152gic_irq_enable(vtimer_irq);153gic_irq_enable(ptimer_irq);154local_irq_enable();155156guest_run_stage(shared_data, GUEST_STAGE_VTIMER_CVAL);157guest_run_stage(shared_data, GUEST_STAGE_VTIMER_TVAL);158guest_run_stage(shared_data, GUEST_STAGE_PTIMER_CVAL);159guest_run_stage(shared_data, GUEST_STAGE_PTIMER_TVAL);160161GUEST_DONE();162}163164static void test_init_timer_irq(struct kvm_vm *vm)165{166/* Timer initid should be same for all the vCPUs, so query only vCPU-0 */167ptimer_irq = vcpu_get_ptimer_irq(vcpus[0]);168vtimer_irq = vcpu_get_vtimer_irq(vcpus[0]);169170sync_global_to_guest(vm, ptimer_irq);171sync_global_to_guest(vm, vtimer_irq);172173pr_debug("ptimer_irq: %d; vtimer_irq: %d\n", ptimer_irq, vtimer_irq);174}175176struct kvm_vm *test_vm_create(void)177{178struct kvm_vm *vm;179unsigned int i;180int nr_vcpus = test_args.nr_vcpus;181182TEST_REQUIRE(kvm_supports_vgic_v3());183184vm = vm_create_with_vcpus(nr_vcpus, guest_code, vcpus);185186vm_init_descriptor_tables(vm);187vm_install_exception_handler(vm, VECTOR_IRQ_CURRENT, guest_irq_handler);188189if (!test_args.reserved) {190if (kvm_has_cap(KVM_CAP_COUNTER_OFFSET)) {191struct kvm_arm_counter_offset offset = {192.counter_offset = test_args.counter_offset,193.reserved = 0,194};195vm_ioctl(vm, KVM_ARM_SET_COUNTER_OFFSET, &offset);196} else197TEST_FAIL("no support for global offset");198}199200for (i = 0; i < nr_vcpus; i++)201vcpu_init_descriptor_tables(vcpus[i]);202203test_init_timer_irq(vm);204205/* Make all the test's cmdline args visible to the guest */206sync_global_to_guest(vm, test_args);207208return vm;209}210211void test_vm_cleanup(struct kvm_vm *vm)212{213kvm_vm_free(vm);214}215216217