1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * arch_timer.c - Tests the riscv64 sstc timer IRQ functionality 4 * 5 * The test validates the sstc timer IRQs using vstimecmp registers. 6 * It's ported from the aarch64 arch_timer test. 7 * 8 * Copyright (c) 2024, Intel Corporation. 9 */ 10 11 #define _GNU_SOURCE 12 13 #include "arch_timer.h" 14 #include "kvm_util.h" 15 #include "processor.h" 16 #include "timer_test.h" 17 18 static int timer_irq = IRQ_S_TIMER; 19 20 static void guest_irq_handler(struct ex_regs *regs) 21 { 22 uint64_t xcnt, xcnt_diff_us, cmp; 23 unsigned int intid = regs->cause & ~CAUSE_IRQ_FLAG; 24 uint32_t cpu = guest_get_vcpuid(); 25 struct test_vcpu_shared_data *shared_data = &vcpu_shared_data[cpu]; 26 27 timer_irq_disable(); 28 29 xcnt = timer_get_cycles(); 30 cmp = timer_get_cmp(); 31 xcnt_diff_us = cycles_to_usec(xcnt - shared_data->xcnt); 32 33 /* Make sure we are dealing with the correct timer IRQ */ 34 GUEST_ASSERT_EQ(intid, timer_irq); 35 36 __GUEST_ASSERT(xcnt >= cmp, 37 "xcnt = 0x%"PRIx64", cmp = 0x%"PRIx64", xcnt_diff_us = 0x%" PRIx64, 38 xcnt, cmp, xcnt_diff_us); 39 40 WRITE_ONCE(shared_data->nr_iter, shared_data->nr_iter + 1); 41 } 42 43 static void guest_run(struct test_vcpu_shared_data *shared_data) 44 { 45 uint32_t irq_iter, config_iter; 46 47 shared_data->nr_iter = 0; 48 shared_data->guest_stage = 0; 49 50 for (config_iter = 0; config_iter < test_args.nr_iter; config_iter++) { 51 /* Setup the next interrupt */ 52 timer_set_next_cmp_ms(test_args.timer_period_ms); 53 shared_data->xcnt = timer_get_cycles(); 54 timer_irq_enable(); 55 56 /* Setup a timeout for the interrupt to arrive */ 57 udelay(msecs_to_usecs(test_args.timer_period_ms) + 58 test_args.timer_err_margin_us); 59 60 irq_iter = READ_ONCE(shared_data->nr_iter); 61 __GUEST_ASSERT(config_iter + 1 == irq_iter, 62 "config_iter + 1 = 0x%x, irq_iter = 0x%x.\n" 63 " Guest timer interrupt was not triggered within the specified\n" 64 " interval, try to increase the error margin by [-e] option.\n", 65 config_iter + 1, irq_iter); 66 } 67 } 68 69 static void guest_code(void) 70 { 71 uint32_t cpu = guest_get_vcpuid(); 72 struct test_vcpu_shared_data *shared_data = &vcpu_shared_data[cpu]; 73 74 timer_irq_disable(); 75 local_irq_enable(); 76 77 guest_run(shared_data); 78 79 GUEST_DONE(); 80 } 81 82 struct kvm_vm *test_vm_create(void) 83 { 84 struct kvm_vm *vm; 85 int nr_vcpus = test_args.nr_vcpus; 86 87 vm = vm_create_with_vcpus(nr_vcpus, guest_code, vcpus); 88 __TEST_REQUIRE(__vcpu_has_ext(vcpus[0], RISCV_ISA_EXT_REG(KVM_RISCV_ISA_EXT_SSTC)), 89 "SSTC not available, skipping test\n"); 90 91 vm_init_vector_tables(vm); 92 vm_install_interrupt_handler(vm, guest_irq_handler); 93 94 for (int i = 0; i < nr_vcpus; i++) 95 vcpu_init_vector_tables(vcpus[i]); 96 97 /* Initialize guest timer frequency. */ 98 vcpu_get_reg(vcpus[0], RISCV_TIMER_REG(frequency), &timer_freq); 99 sync_global_to_guest(vm, timer_freq); 100 pr_debug("timer_freq: %lu\n", timer_freq); 101 102 /* Make all the test's cmdline args visible to the guest */ 103 sync_global_to_guest(vm, test_args); 104 105 return vm; 106 } 107 108 void test_vm_cleanup(struct kvm_vm *vm) 109 { 110 kvm_vm_free(vm); 111 } 112