Path: blob/master/tools/testing/selftests/kvm/loongarch/arch_timer.c
38245 views
// SPDX-License-Identifier: GPL-2.0-only1/*2* The test validates periodic/one-shot constant timer IRQ using3* CSR.TCFG and CSR.TVAL registers.4*/5#include "arch_timer.h"6#include "kvm_util.h"7#include "processor.h"8#include "timer_test.h"9#include "ucall_common.h"1011static void do_idle(void)12{13unsigned int intid;14unsigned long estat;1516__asm__ __volatile__("idle 0" : : : "memory");1718estat = csr_read(LOONGARCH_CSR_ESTAT);19intid = !!(estat & BIT(INT_TI));2021/* Make sure pending timer IRQ arrived */22GUEST_ASSERT_EQ(intid, 1);23csr_write(CSR_TINTCLR_TI, LOONGARCH_CSR_TINTCLR);24}2526static void guest_irq_handler(struct ex_regs *regs)27{28unsigned int intid;29uint32_t cpu = guest_get_vcpuid();30uint64_t xcnt, val, cfg, xcnt_diff_us;31struct test_vcpu_shared_data *shared_data = &vcpu_shared_data[cpu];3233intid = !!(regs->estat & BIT(INT_TI));3435/* Make sure we are dealing with the correct timer IRQ */36GUEST_ASSERT_EQ(intid, 1);3738cfg = timer_get_cfg();39if (cfg & CSR_TCFG_PERIOD) {40WRITE_ONCE(shared_data->nr_iter, shared_data->nr_iter - 1);41if (shared_data->nr_iter == 0)42disable_timer();43csr_write(CSR_TINTCLR_TI, LOONGARCH_CSR_TINTCLR);44return;45}4647/*48* On real machine, value of LOONGARCH_CSR_TVAL is BIT_ULL(48) - 149* On virtual machine, its value counts down from BIT_ULL(48) - 150*/51val = timer_get_val();52xcnt = timer_get_cycles();53xcnt_diff_us = cycles_to_usec(xcnt - shared_data->xcnt);5455/* Basic 'timer condition met' check */56__GUEST_ASSERT(val > cfg,57"val = 0x%lx, cfg = 0x%lx, xcnt_diff_us = 0x%lx",58val, cfg, xcnt_diff_us);5960csr_write(CSR_TINTCLR_TI, LOONGARCH_CSR_TINTCLR);61WRITE_ONCE(shared_data->nr_iter, shared_data->nr_iter + 1);62}6364static void guest_test_period_timer(uint32_t cpu)65{66uint32_t irq_iter, config_iter;67uint64_t us;68struct test_vcpu_shared_data *shared_data = &vcpu_shared_data[cpu];6970shared_data->nr_iter = test_args.nr_iter;71shared_data->xcnt = timer_get_cycles();72us = msecs_to_usecs(test_args.timer_period_ms) + test_args.timer_err_margin_us;73timer_set_next_cmp_ms(test_args.timer_period_ms, true);7475for (config_iter = 0; config_iter < test_args.nr_iter; config_iter++) {76/* Setup a timeout for the interrupt to arrive */77udelay(us);78}7980irq_iter = READ_ONCE(shared_data->nr_iter);81__GUEST_ASSERT(irq_iter == 0,82"irq_iter = 0x%x.\n"83" Guest period timer interrupt was not triggered within the specified\n"84" interval, try to increase the error margin by [-e] option.\n",85irq_iter);86}8788static void guest_test_oneshot_timer(uint32_t cpu)89{90uint32_t irq_iter, config_iter;91uint64_t us;92struct test_vcpu_shared_data *shared_data = &vcpu_shared_data[cpu];9394shared_data->nr_iter = 0;95shared_data->guest_stage = 0;96us = msecs_to_usecs(test_args.timer_period_ms) + test_args.timer_err_margin_us;97for (config_iter = 0; config_iter < test_args.nr_iter; config_iter++) {98shared_data->xcnt = timer_get_cycles();99100/* Setup the next interrupt */101timer_set_next_cmp_ms(test_args.timer_period_ms, false);102/* Setup a timeout for the interrupt to arrive */103udelay(us);104105irq_iter = READ_ONCE(shared_data->nr_iter);106__GUEST_ASSERT(config_iter + 1 == irq_iter,107"config_iter + 1 = 0x%x, irq_iter = 0x%x.\n"108" Guest timer interrupt was not triggered within the specified\n"109" interval, try to increase the error margin by [-e] option.\n",110config_iter + 1, irq_iter);111}112}113114static void guest_test_emulate_timer(uint32_t cpu)115{116uint32_t config_iter;117uint64_t xcnt_diff_us, us;118struct test_vcpu_shared_data *shared_data = &vcpu_shared_data[cpu];119120local_irq_disable();121shared_data->nr_iter = 0;122us = msecs_to_usecs(test_args.timer_period_ms);123for (config_iter = 0; config_iter < test_args.nr_iter; config_iter++) {124shared_data->xcnt = timer_get_cycles();125126/* Setup the next interrupt */127timer_set_next_cmp_ms(test_args.timer_period_ms, false);128do_idle();129130xcnt_diff_us = cycles_to_usec(timer_get_cycles() - shared_data->xcnt);131__GUEST_ASSERT(xcnt_diff_us >= us,132"xcnt_diff_us = 0x%lx, us = 0x%lx.\n",133xcnt_diff_us, us);134}135local_irq_enable();136}137138static void guest_time_count_test(uint32_t cpu)139{140uint32_t config_iter;141unsigned long start, end, prev, us;142143/* Assuming that test case starts to run in 1 second */144start = timer_get_cycles();145us = msec_to_cycles(1000);146__GUEST_ASSERT(start <= us,147"start = 0x%lx, us = 0x%lx.\n",148start, us);149150us = msec_to_cycles(test_args.timer_period_ms);151for (config_iter = 0; config_iter < test_args.nr_iter; config_iter++) {152start = timer_get_cycles();153end = start + us;154/* test time count growing up always */155while (start < end) {156prev = start;157start = timer_get_cycles();158__GUEST_ASSERT(prev <= start,159"prev = 0x%lx, start = 0x%lx.\n",160prev, start);161}162}163}164165static void guest_code(void)166{167uint32_t cpu = guest_get_vcpuid();168169/* must run at first */170guest_time_count_test(cpu);171172timer_irq_enable();173local_irq_enable();174guest_test_period_timer(cpu);175guest_test_oneshot_timer(cpu);176guest_test_emulate_timer(cpu);177178GUEST_DONE();179}180181struct kvm_vm *test_vm_create(void)182{183struct kvm_vm *vm;184int nr_vcpus = test_args.nr_vcpus;185186vm = vm_create_with_vcpus(nr_vcpus, guest_code, vcpus);187vm_init_descriptor_tables(vm);188vm_install_exception_handler(vm, EXCCODE_INT, guest_irq_handler);189190/* Make all the test's cmdline args visible to the guest */191sync_global_to_guest(vm, test_args);192193return vm;194}195196void test_vm_cleanup(struct kvm_vm *vm)197{198kvm_vm_free(vm);199}200201202