Loading tools/testing/selftests/kvm/Makefile.kvm +1 −0 Original line number Diff line number Diff line Loading @@ -210,6 +210,7 @@ TEST_GEN_PROGS_riscv += mmu_stress_test TEST_GEN_PROGS_riscv += rseq_test TEST_GEN_PROGS_riscv += steal_time TEST_GEN_PROGS_loongarch = arch_timer TEST_GEN_PROGS_loongarch += coalesced_io_test TEST_GEN_PROGS_loongarch += demand_paging_test TEST_GEN_PROGS_loongarch += dirty_log_perf_test Loading tools/testing/selftests/kvm/include/loongarch/arch_timer.h 0 → 100644 +85 −0 Original line number Diff line number Diff line /* SPDX-License-Identifier: GPL-2.0 */ /* * LoongArch Constant Timer specific interface */ #ifndef SELFTEST_KVM_ARCH_TIMER_H #define SELFTEST_KVM_ARCH_TIMER_H #include "processor.h" /* LoongArch timer frequency is constant 100MHZ */ #define TIMER_FREQ (100UL << 20) #define msec_to_cycles(msec) (TIMER_FREQ * (unsigned long)(msec) / 1000) #define usec_to_cycles(usec) (TIMER_FREQ * (unsigned long)(usec) / 1000000) #define cycles_to_usec(cycles) ((unsigned long)(cycles) * 1000000 / TIMER_FREQ) static inline unsigned long timer_get_cycles(void) { unsigned long val = 0; __asm__ __volatile__( "rdtime.d %0, $zero\n\t" : "=r"(val) : ); return val; } static inline unsigned long timer_get_cfg(void) { return csr_read(LOONGARCH_CSR_TCFG); } static inline unsigned long timer_get_val(void) { return csr_read(LOONGARCH_CSR_TVAL); } static inline void disable_timer(void) { csr_write(0, LOONGARCH_CSR_TCFG); } static inline void timer_irq_enable(void) { unsigned long val; val = csr_read(LOONGARCH_CSR_ECFG); val |= ECFGF_TIMER; csr_write(val, LOONGARCH_CSR_ECFG); } static inline void timer_irq_disable(void) { unsigned long val; val = csr_read(LOONGARCH_CSR_ECFG); val &= ~ECFGF_TIMER; csr_write(val, LOONGARCH_CSR_ECFG); } static inline void timer_set_next_cmp_ms(unsigned int msec, bool period) { unsigned long val; val = msec_to_cycles(msec) & CSR_TCFG_VAL; val |= CSR_TCFG_EN; if (period) val |= CSR_TCFG_PERIOD; csr_write(val, LOONGARCH_CSR_TCFG); } static inline void __delay(uint64_t cycles) { uint64_t start = timer_get_cycles(); while ((timer_get_cycles() - start) < cycles) cpu_relax(); } static inline void udelay(unsigned long usec) { __delay(usec_to_cycles(usec)); } #endif /* SELFTEST_KVM_ARCH_TIMER_H */ tools/testing/selftests/kvm/include/loongarch/processor.h +10 −0 Original line number Diff line number Diff line Loading @@ -83,6 +83,8 @@ #define LOONGARCH_CSR_PRMD 0x1 #define LOONGARCH_CSR_EUEN 0x2 #define LOONGARCH_CSR_ECFG 0x4 #define ECFGB_TIMER 11 #define ECFGF_TIMER (BIT_ULL(ECFGB_TIMER)) #define LOONGARCH_CSR_ESTAT 0x5 /* Exception status */ #define CSR_ESTAT_EXC_SHIFT 16 #define CSR_ESTAT_EXC_WIDTH 6 Loading Loading @@ -111,6 +113,14 @@ #define LOONGARCH_CSR_KS1 0x31 #define LOONGARCH_CSR_TMID 0x40 #define LOONGARCH_CSR_TCFG 0x41 #define CSR_TCFG_VAL (BIT_ULL(48) - BIT_ULL(2)) #define CSR_TCFG_PERIOD_SHIFT 1 #define CSR_TCFG_PERIOD (0x1UL << CSR_TCFG_PERIOD_SHIFT) #define CSR_TCFG_EN (0x1UL) #define LOONGARCH_CSR_TVAL 0x42 #define LOONGARCH_CSR_TINTCLR 0x44 /* Timer interrupt clear */ #define CSR_TINTCLR_TI_SHIFT 0 #define CSR_TINTCLR_TI (1 << CSR_TINTCLR_TI_SHIFT) /* TLB refill exception entry */ #define LOONGARCH_CSR_TLBRENTRY 0x88 #define LOONGARCH_CSR_TLBRSAVE 0x8b Loading tools/testing/selftests/kvm/lib/loongarch/processor.c +2 −2 Original line number Diff line number Diff line Loading @@ -276,8 +276,8 @@ static void loongarch_vcpu_setup(struct kvm_vcpu *vcpu) TEST_FAIL("Unknown guest mode, mode: 0x%x", vm->mode); } /* user mode and page enable mode */ val = PLV_USER | CSR_CRMD_PG; /* kernel mode and page enable mode */ val = PLV_KERN | CSR_CRMD_PG; loongarch_set_csr(vcpu, LOONGARCH_CSR_CRMD, val); loongarch_set_csr(vcpu, LOONGARCH_CSR_PRMD, val); loongarch_set_csr(vcpu, LOONGARCH_CSR_EUEN, 1); Loading tools/testing/selftests/kvm/loongarch/arch_timer.c 0 → 100644 +130 −0 Original line number Diff line number Diff line // SPDX-License-Identifier: GPL-2.0-only /* * The test validates periodic/one-shot constant timer IRQ using * CSR.TCFG and CSR.TVAL registers. */ #include "arch_timer.h" #include "kvm_util.h" #include "processor.h" #include "timer_test.h" #include "ucall_common.h" static void guest_irq_handler(struct ex_regs *regs) { unsigned int intid; uint32_t cpu = guest_get_vcpuid(); uint64_t xcnt, val, cfg, xcnt_diff_us; struct test_vcpu_shared_data *shared_data = &vcpu_shared_data[cpu]; intid = !!(regs->estat & BIT(INT_TI)); /* Make sure we are dealing with the correct timer IRQ */ GUEST_ASSERT_EQ(intid, 1); cfg = timer_get_cfg(); if (cfg & CSR_TCFG_PERIOD) { WRITE_ONCE(shared_data->nr_iter, shared_data->nr_iter - 1); if (shared_data->nr_iter == 0) disable_timer(); csr_write(CSR_TINTCLR_TI, LOONGARCH_CSR_TINTCLR); return; } /* * On real machine, value of LOONGARCH_CSR_TVAL is BIT_ULL(48) - 1 * On virtual machine, its value counts down from BIT_ULL(48) - 1 */ val = timer_get_val(); xcnt = timer_get_cycles(); xcnt_diff_us = cycles_to_usec(xcnt - shared_data->xcnt); /* Basic 'timer condition met' check */ __GUEST_ASSERT(val > cfg, "val = 0x%lx, cfg = 0x%lx, xcnt_diff_us = 0x%lx", val, cfg, xcnt_diff_us); csr_write(CSR_TINTCLR_TI, LOONGARCH_CSR_TINTCLR); WRITE_ONCE(shared_data->nr_iter, shared_data->nr_iter + 1); } static void guest_test_period_timer(uint32_t cpu) { uint32_t irq_iter, config_iter; uint64_t us; struct test_vcpu_shared_data *shared_data = &vcpu_shared_data[cpu]; shared_data->nr_iter = test_args.nr_iter; shared_data->xcnt = timer_get_cycles(); us = msecs_to_usecs(test_args.timer_period_ms) + test_args.timer_err_margin_us; timer_set_next_cmp_ms(test_args.timer_period_ms, true); for (config_iter = 0; config_iter < test_args.nr_iter; config_iter++) { /* Setup a timeout for the interrupt to arrive */ udelay(us); } irq_iter = READ_ONCE(shared_data->nr_iter); __GUEST_ASSERT(irq_iter == 0, "irq_iter = 0x%x.\n" " Guest period timer interrupt was not triggered within the specified\n" " interval, try to increase the error margin by [-e] option.\n", irq_iter); } static void guest_test_oneshot_timer(uint32_t cpu) { uint32_t irq_iter, config_iter; uint64_t us; struct test_vcpu_shared_data *shared_data = &vcpu_shared_data[cpu]; shared_data->nr_iter = 0; shared_data->guest_stage = 0; us = msecs_to_usecs(test_args.timer_period_ms) + test_args.timer_err_margin_us; for (config_iter = 0; config_iter < test_args.nr_iter; config_iter++) { shared_data->xcnt = timer_get_cycles(); /* Setup the next interrupt */ timer_set_next_cmp_ms(test_args.timer_period_ms, false); /* Setup a timeout for the interrupt to arrive */ udelay(us); irq_iter = READ_ONCE(shared_data->nr_iter); __GUEST_ASSERT(config_iter + 1 == irq_iter, "config_iter + 1 = 0x%x, irq_iter = 0x%x.\n" " Guest timer interrupt was not triggered within the specified\n" " interval, try to increase the error margin by [-e] option.\n", config_iter + 1, irq_iter); } } static void guest_code(void) { uint32_t cpu = guest_get_vcpuid(); timer_irq_enable(); local_irq_enable(); guest_test_period_timer(cpu); guest_test_oneshot_timer(cpu); GUEST_DONE(); } struct kvm_vm *test_vm_create(void) { struct kvm_vm *vm; int nr_vcpus = test_args.nr_vcpus; vm = vm_create_with_vcpus(nr_vcpus, guest_code, vcpus); vm_init_descriptor_tables(vm); vm_install_exception_handler(vm, EXCCODE_INT, guest_irq_handler); /* Make all the test's cmdline args visible to the guest */ sync_global_to_guest(vm, test_args); return vm; } void test_vm_cleanup(struct kvm_vm *vm) { kvm_vm_free(vm); } Loading
tools/testing/selftests/kvm/Makefile.kvm +1 −0 Original line number Diff line number Diff line Loading @@ -210,6 +210,7 @@ TEST_GEN_PROGS_riscv += mmu_stress_test TEST_GEN_PROGS_riscv += rseq_test TEST_GEN_PROGS_riscv += steal_time TEST_GEN_PROGS_loongarch = arch_timer TEST_GEN_PROGS_loongarch += coalesced_io_test TEST_GEN_PROGS_loongarch += demand_paging_test TEST_GEN_PROGS_loongarch += dirty_log_perf_test Loading
tools/testing/selftests/kvm/include/loongarch/arch_timer.h 0 → 100644 +85 −0 Original line number Diff line number Diff line /* SPDX-License-Identifier: GPL-2.0 */ /* * LoongArch Constant Timer specific interface */ #ifndef SELFTEST_KVM_ARCH_TIMER_H #define SELFTEST_KVM_ARCH_TIMER_H #include "processor.h" /* LoongArch timer frequency is constant 100MHZ */ #define TIMER_FREQ (100UL << 20) #define msec_to_cycles(msec) (TIMER_FREQ * (unsigned long)(msec) / 1000) #define usec_to_cycles(usec) (TIMER_FREQ * (unsigned long)(usec) / 1000000) #define cycles_to_usec(cycles) ((unsigned long)(cycles) * 1000000 / TIMER_FREQ) static inline unsigned long timer_get_cycles(void) { unsigned long val = 0; __asm__ __volatile__( "rdtime.d %0, $zero\n\t" : "=r"(val) : ); return val; } static inline unsigned long timer_get_cfg(void) { return csr_read(LOONGARCH_CSR_TCFG); } static inline unsigned long timer_get_val(void) { return csr_read(LOONGARCH_CSR_TVAL); } static inline void disable_timer(void) { csr_write(0, LOONGARCH_CSR_TCFG); } static inline void timer_irq_enable(void) { unsigned long val; val = csr_read(LOONGARCH_CSR_ECFG); val |= ECFGF_TIMER; csr_write(val, LOONGARCH_CSR_ECFG); } static inline void timer_irq_disable(void) { unsigned long val; val = csr_read(LOONGARCH_CSR_ECFG); val &= ~ECFGF_TIMER; csr_write(val, LOONGARCH_CSR_ECFG); } static inline void timer_set_next_cmp_ms(unsigned int msec, bool period) { unsigned long val; val = msec_to_cycles(msec) & CSR_TCFG_VAL; val |= CSR_TCFG_EN; if (period) val |= CSR_TCFG_PERIOD; csr_write(val, LOONGARCH_CSR_TCFG); } static inline void __delay(uint64_t cycles) { uint64_t start = timer_get_cycles(); while ((timer_get_cycles() - start) < cycles) cpu_relax(); } static inline void udelay(unsigned long usec) { __delay(usec_to_cycles(usec)); } #endif /* SELFTEST_KVM_ARCH_TIMER_H */
tools/testing/selftests/kvm/include/loongarch/processor.h +10 −0 Original line number Diff line number Diff line Loading @@ -83,6 +83,8 @@ #define LOONGARCH_CSR_PRMD 0x1 #define LOONGARCH_CSR_EUEN 0x2 #define LOONGARCH_CSR_ECFG 0x4 #define ECFGB_TIMER 11 #define ECFGF_TIMER (BIT_ULL(ECFGB_TIMER)) #define LOONGARCH_CSR_ESTAT 0x5 /* Exception status */ #define CSR_ESTAT_EXC_SHIFT 16 #define CSR_ESTAT_EXC_WIDTH 6 Loading Loading @@ -111,6 +113,14 @@ #define LOONGARCH_CSR_KS1 0x31 #define LOONGARCH_CSR_TMID 0x40 #define LOONGARCH_CSR_TCFG 0x41 #define CSR_TCFG_VAL (BIT_ULL(48) - BIT_ULL(2)) #define CSR_TCFG_PERIOD_SHIFT 1 #define CSR_TCFG_PERIOD (0x1UL << CSR_TCFG_PERIOD_SHIFT) #define CSR_TCFG_EN (0x1UL) #define LOONGARCH_CSR_TVAL 0x42 #define LOONGARCH_CSR_TINTCLR 0x44 /* Timer interrupt clear */ #define CSR_TINTCLR_TI_SHIFT 0 #define CSR_TINTCLR_TI (1 << CSR_TINTCLR_TI_SHIFT) /* TLB refill exception entry */ #define LOONGARCH_CSR_TLBRENTRY 0x88 #define LOONGARCH_CSR_TLBRSAVE 0x8b Loading
tools/testing/selftests/kvm/lib/loongarch/processor.c +2 −2 Original line number Diff line number Diff line Loading @@ -276,8 +276,8 @@ static void loongarch_vcpu_setup(struct kvm_vcpu *vcpu) TEST_FAIL("Unknown guest mode, mode: 0x%x", vm->mode); } /* user mode and page enable mode */ val = PLV_USER | CSR_CRMD_PG; /* kernel mode and page enable mode */ val = PLV_KERN | CSR_CRMD_PG; loongarch_set_csr(vcpu, LOONGARCH_CSR_CRMD, val); loongarch_set_csr(vcpu, LOONGARCH_CSR_PRMD, val); loongarch_set_csr(vcpu, LOONGARCH_CSR_EUEN, 1); Loading
tools/testing/selftests/kvm/loongarch/arch_timer.c 0 → 100644 +130 −0 Original line number Diff line number Diff line // SPDX-License-Identifier: GPL-2.0-only /* * The test validates periodic/one-shot constant timer IRQ using * CSR.TCFG and CSR.TVAL registers. */ #include "arch_timer.h" #include "kvm_util.h" #include "processor.h" #include "timer_test.h" #include "ucall_common.h" static void guest_irq_handler(struct ex_regs *regs) { unsigned int intid; uint32_t cpu = guest_get_vcpuid(); uint64_t xcnt, val, cfg, xcnt_diff_us; struct test_vcpu_shared_data *shared_data = &vcpu_shared_data[cpu]; intid = !!(regs->estat & BIT(INT_TI)); /* Make sure we are dealing with the correct timer IRQ */ GUEST_ASSERT_EQ(intid, 1); cfg = timer_get_cfg(); if (cfg & CSR_TCFG_PERIOD) { WRITE_ONCE(shared_data->nr_iter, shared_data->nr_iter - 1); if (shared_data->nr_iter == 0) disable_timer(); csr_write(CSR_TINTCLR_TI, LOONGARCH_CSR_TINTCLR); return; } /* * On real machine, value of LOONGARCH_CSR_TVAL is BIT_ULL(48) - 1 * On virtual machine, its value counts down from BIT_ULL(48) - 1 */ val = timer_get_val(); xcnt = timer_get_cycles(); xcnt_diff_us = cycles_to_usec(xcnt - shared_data->xcnt); /* Basic 'timer condition met' check */ __GUEST_ASSERT(val > cfg, "val = 0x%lx, cfg = 0x%lx, xcnt_diff_us = 0x%lx", val, cfg, xcnt_diff_us); csr_write(CSR_TINTCLR_TI, LOONGARCH_CSR_TINTCLR); WRITE_ONCE(shared_data->nr_iter, shared_data->nr_iter + 1); } static void guest_test_period_timer(uint32_t cpu) { uint32_t irq_iter, config_iter; uint64_t us; struct test_vcpu_shared_data *shared_data = &vcpu_shared_data[cpu]; shared_data->nr_iter = test_args.nr_iter; shared_data->xcnt = timer_get_cycles(); us = msecs_to_usecs(test_args.timer_period_ms) + test_args.timer_err_margin_us; timer_set_next_cmp_ms(test_args.timer_period_ms, true); for (config_iter = 0; config_iter < test_args.nr_iter; config_iter++) { /* Setup a timeout for the interrupt to arrive */ udelay(us); } irq_iter = READ_ONCE(shared_data->nr_iter); __GUEST_ASSERT(irq_iter == 0, "irq_iter = 0x%x.\n" " Guest period timer interrupt was not triggered within the specified\n" " interval, try to increase the error margin by [-e] option.\n", irq_iter); } static void guest_test_oneshot_timer(uint32_t cpu) { uint32_t irq_iter, config_iter; uint64_t us; struct test_vcpu_shared_data *shared_data = &vcpu_shared_data[cpu]; shared_data->nr_iter = 0; shared_data->guest_stage = 0; us = msecs_to_usecs(test_args.timer_period_ms) + test_args.timer_err_margin_us; for (config_iter = 0; config_iter < test_args.nr_iter; config_iter++) { shared_data->xcnt = timer_get_cycles(); /* Setup the next interrupt */ timer_set_next_cmp_ms(test_args.timer_period_ms, false); /* Setup a timeout for the interrupt to arrive */ udelay(us); irq_iter = READ_ONCE(shared_data->nr_iter); __GUEST_ASSERT(config_iter + 1 == irq_iter, "config_iter + 1 = 0x%x, irq_iter = 0x%x.\n" " Guest timer interrupt was not triggered within the specified\n" " interval, try to increase the error margin by [-e] option.\n", config_iter + 1, irq_iter); } } static void guest_code(void) { uint32_t cpu = guest_get_vcpuid(); timer_irq_enable(); local_irq_enable(); guest_test_period_timer(cpu); guest_test_oneshot_timer(cpu); GUEST_DONE(); } struct kvm_vm *test_vm_create(void) { struct kvm_vm *vm; int nr_vcpus = test_args.nr_vcpus; vm = vm_create_with_vcpus(nr_vcpus, guest_code, vcpus); vm_init_descriptor_tables(vm); vm_install_exception_handler(vm, EXCCODE_INT, guest_irq_handler); /* Make all the test's cmdline args visible to the guest */ sync_global_to_guest(vm, test_args); return vm; } void test_vm_cleanup(struct kvm_vm *vm) { kvm_vm_free(vm); }