Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

KVM: LoongArch: selftests: Add timer interrupt test case

Add timer test case based on common arch_timer code, timer interrupt
with one-shot and period mode is tested.

Signed-off-by: Bibo Mao <maobibo@loongson.cn>
Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>

authored by

Bibo Mao and committed by
Huacai Chen
df417423 d84fe2f3

+228 -2
+1
tools/testing/selftests/kvm/Makefile.kvm
··· 210 210 TEST_GEN_PROGS_riscv += rseq_test 211 211 TEST_GEN_PROGS_riscv += steal_time 212 212 213 + TEST_GEN_PROGS_loongarch = arch_timer 213 214 TEST_GEN_PROGS_loongarch += coalesced_io_test 214 215 TEST_GEN_PROGS_loongarch += demand_paging_test 215 216 TEST_GEN_PROGS_loongarch += dirty_log_perf_test
+85
tools/testing/selftests/kvm/include/loongarch/arch_timer.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + /* 3 + * LoongArch Constant Timer specific interface 4 + */ 5 + #ifndef SELFTEST_KVM_ARCH_TIMER_H 6 + #define SELFTEST_KVM_ARCH_TIMER_H 7 + 8 + #include "processor.h" 9 + 10 + /* LoongArch timer frequency is constant 100MHZ */ 11 + #define TIMER_FREQ (100UL << 20) 12 + #define msec_to_cycles(msec) (TIMER_FREQ * (unsigned long)(msec) / 1000) 13 + #define usec_to_cycles(usec) (TIMER_FREQ * (unsigned long)(usec) / 1000000) 14 + #define cycles_to_usec(cycles) ((unsigned long)(cycles) * 1000000 / TIMER_FREQ) 15 + 16 + static inline unsigned long timer_get_cycles(void) 17 + { 18 + unsigned long val = 0; 19 + 20 + __asm__ __volatile__( 21 + "rdtime.d %0, $zero\n\t" 22 + : "=r"(val) 23 + : 24 + ); 25 + 26 + return val; 27 + } 28 + 29 + static inline unsigned long timer_get_cfg(void) 30 + { 31 + return csr_read(LOONGARCH_CSR_TCFG); 32 + } 33 + 34 + static inline unsigned long timer_get_val(void) 35 + { 36 + return csr_read(LOONGARCH_CSR_TVAL); 37 + } 38 + 39 + static inline void disable_timer(void) 40 + { 41 + csr_write(0, LOONGARCH_CSR_TCFG); 42 + } 43 + 44 + static inline void timer_irq_enable(void) 45 + { 46 + unsigned long val; 47 + 48 + val = csr_read(LOONGARCH_CSR_ECFG); 49 + val |= ECFGF_TIMER; 50 + csr_write(val, LOONGARCH_CSR_ECFG); 51 + } 52 + 53 + static inline void timer_irq_disable(void) 54 + { 55 + unsigned long val; 56 + 57 + val = csr_read(LOONGARCH_CSR_ECFG); 58 + val &= ~ECFGF_TIMER; 59 + csr_write(val, LOONGARCH_CSR_ECFG); 60 + } 61 + 62 + static inline void timer_set_next_cmp_ms(unsigned int msec, bool period) 63 + { 64 + unsigned long val; 65 + 66 + val = msec_to_cycles(msec) & CSR_TCFG_VAL; 67 + val |= CSR_TCFG_EN; 68 + if (period) 69 + val |= CSR_TCFG_PERIOD; 70 + csr_write(val, LOONGARCH_CSR_TCFG); 71 + } 72 + 73 + static inline void __delay(uint64_t cycles) 74 + { 75 + uint64_t start = timer_get_cycles(); 76 + 77 + while ((timer_get_cycles() - start) < cycles) 78 + cpu_relax(); 79 + } 80 + 81 + static inline void udelay(unsigned long usec) 82 + { 83 + __delay(usec_to_cycles(usec)); 84 + } 85 + #endif /* SELFTEST_KVM_ARCH_TIMER_H */
+10
tools/testing/selftests/kvm/include/loongarch/processor.h
··· 83 83 #define LOONGARCH_CSR_PRMD 0x1 84 84 #define LOONGARCH_CSR_EUEN 0x2 85 85 #define LOONGARCH_CSR_ECFG 0x4 86 + #define ECFGB_TIMER 11 87 + #define ECFGF_TIMER (BIT_ULL(ECFGB_TIMER)) 86 88 #define LOONGARCH_CSR_ESTAT 0x5 /* Exception status */ 87 89 #define CSR_ESTAT_EXC_SHIFT 16 88 90 #define CSR_ESTAT_EXC_WIDTH 6 ··· 113 111 #define LOONGARCH_CSR_KS1 0x31 114 112 #define LOONGARCH_CSR_TMID 0x40 115 113 #define LOONGARCH_CSR_TCFG 0x41 114 + #define CSR_TCFG_VAL (BIT_ULL(48) - BIT_ULL(2)) 115 + #define CSR_TCFG_PERIOD_SHIFT 1 116 + #define CSR_TCFG_PERIOD (0x1UL << CSR_TCFG_PERIOD_SHIFT) 117 + #define CSR_TCFG_EN (0x1UL) 118 + #define LOONGARCH_CSR_TVAL 0x42 119 + #define LOONGARCH_CSR_TINTCLR 0x44 /* Timer interrupt clear */ 120 + #define CSR_TINTCLR_TI_SHIFT 0 121 + #define CSR_TINTCLR_TI (1 << CSR_TINTCLR_TI_SHIFT) 116 122 /* TLB refill exception entry */ 117 123 #define LOONGARCH_CSR_TLBRENTRY 0x88 118 124 #define LOONGARCH_CSR_TLBRSAVE 0x8b
+2 -2
tools/testing/selftests/kvm/lib/loongarch/processor.c
··· 276 276 TEST_FAIL("Unknown guest mode, mode: 0x%x", vm->mode); 277 277 } 278 278 279 - /* user mode and page enable mode */ 280 - val = PLV_USER | CSR_CRMD_PG; 279 + /* kernel mode and page enable mode */ 280 + val = PLV_KERN | CSR_CRMD_PG; 281 281 loongarch_set_csr(vcpu, LOONGARCH_CSR_CRMD, val); 282 282 loongarch_set_csr(vcpu, LOONGARCH_CSR_PRMD, val); 283 283 loongarch_set_csr(vcpu, LOONGARCH_CSR_EUEN, 1);
+130
tools/testing/selftests/kvm/loongarch/arch_timer.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 + 12 + static void guest_irq_handler(struct ex_regs *regs) 13 + { 14 + unsigned int intid; 15 + uint32_t cpu = guest_get_vcpuid(); 16 + uint64_t xcnt, val, cfg, xcnt_diff_us; 17 + struct test_vcpu_shared_data *shared_data = &vcpu_shared_data[cpu]; 18 + 19 + intid = !!(regs->estat & BIT(INT_TI)); 20 + 21 + /* Make sure we are dealing with the correct timer IRQ */ 22 + GUEST_ASSERT_EQ(intid, 1); 23 + 24 + cfg = timer_get_cfg(); 25 + if (cfg & CSR_TCFG_PERIOD) { 26 + WRITE_ONCE(shared_data->nr_iter, shared_data->nr_iter - 1); 27 + if (shared_data->nr_iter == 0) 28 + disable_timer(); 29 + csr_write(CSR_TINTCLR_TI, LOONGARCH_CSR_TINTCLR); 30 + return; 31 + } 32 + 33 + /* 34 + * On real machine, value of LOONGARCH_CSR_TVAL is BIT_ULL(48) - 1 35 + * On virtual machine, its value counts down from BIT_ULL(48) - 1 36 + */ 37 + val = timer_get_val(); 38 + xcnt = timer_get_cycles(); 39 + xcnt_diff_us = cycles_to_usec(xcnt - shared_data->xcnt); 40 + 41 + /* Basic 'timer condition met' check */ 42 + __GUEST_ASSERT(val > cfg, 43 + "val = 0x%lx, cfg = 0x%lx, xcnt_diff_us = 0x%lx", 44 + val, cfg, xcnt_diff_us); 45 + 46 + csr_write(CSR_TINTCLR_TI, LOONGARCH_CSR_TINTCLR); 47 + WRITE_ONCE(shared_data->nr_iter, shared_data->nr_iter + 1); 48 + } 49 + 50 + static void guest_test_period_timer(uint32_t cpu) 51 + { 52 + uint32_t irq_iter, config_iter; 53 + uint64_t us; 54 + struct test_vcpu_shared_data *shared_data = &vcpu_shared_data[cpu]; 55 + 56 + shared_data->nr_iter = test_args.nr_iter; 57 + shared_data->xcnt = timer_get_cycles(); 58 + us = msecs_to_usecs(test_args.timer_period_ms) + test_args.timer_err_margin_us; 59 + timer_set_next_cmp_ms(test_args.timer_period_ms, true); 60 + 61 + for (config_iter = 0; config_iter < test_args.nr_iter; config_iter++) { 62 + /* Setup a timeout for the interrupt to arrive */ 63 + udelay(us); 64 + } 65 + 66 + irq_iter = READ_ONCE(shared_data->nr_iter); 67 + __GUEST_ASSERT(irq_iter == 0, 68 + "irq_iter = 0x%x.\n" 69 + " Guest period timer interrupt was not triggered within the specified\n" 70 + " interval, try to increase the error margin by [-e] option.\n", 71 + irq_iter); 72 + } 73 + 74 + static void guest_test_oneshot_timer(uint32_t cpu) 75 + { 76 + uint32_t irq_iter, config_iter; 77 + uint64_t us; 78 + struct test_vcpu_shared_data *shared_data = &vcpu_shared_data[cpu]; 79 + 80 + shared_data->nr_iter = 0; 81 + shared_data->guest_stage = 0; 82 + us = msecs_to_usecs(test_args.timer_period_ms) + test_args.timer_err_margin_us; 83 + for (config_iter = 0; config_iter < test_args.nr_iter; config_iter++) { 84 + shared_data->xcnt = timer_get_cycles(); 85 + 86 + /* Setup the next interrupt */ 87 + timer_set_next_cmp_ms(test_args.timer_period_ms, false); 88 + /* Setup a timeout for the interrupt to arrive */ 89 + udelay(us); 90 + 91 + irq_iter = READ_ONCE(shared_data->nr_iter); 92 + __GUEST_ASSERT(config_iter + 1 == irq_iter, 93 + "config_iter + 1 = 0x%x, irq_iter = 0x%x.\n" 94 + " Guest timer interrupt was not triggered within the specified\n" 95 + " interval, try to increase the error margin by [-e] option.\n", 96 + config_iter + 1, irq_iter); 97 + } 98 + } 99 + 100 + static void guest_code(void) 101 + { 102 + uint32_t cpu = guest_get_vcpuid(); 103 + 104 + timer_irq_enable(); 105 + local_irq_enable(); 106 + guest_test_period_timer(cpu); 107 + guest_test_oneshot_timer(cpu); 108 + 109 + GUEST_DONE(); 110 + } 111 + 112 + struct kvm_vm *test_vm_create(void) 113 + { 114 + struct kvm_vm *vm; 115 + int nr_vcpus = test_args.nr_vcpus; 116 + 117 + vm = vm_create_with_vcpus(nr_vcpus, guest_code, vcpus); 118 + vm_init_descriptor_tables(vm); 119 + vm_install_exception_handler(vm, EXCCODE_INT, guest_irq_handler); 120 + 121 + /* Make all the test's cmdline args visible to the guest */ 122 + sync_global_to_guest(vm, test_args); 123 + 124 + return vm; 125 + } 126 + 127 + void test_vm_cleanup(struct kvm_vm *vm) 128 + { 129 + kvm_vm_free(vm); 130 + }