]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
KVM: LoongArch: selftests: Add PMU overflow interrupt test
authorSong Gao <gaosong@loongson.cn>
Thu, 9 Apr 2026 10:56:38 +0000 (18:56 +0800)
committerHuacai Chen <chenhuacai@loongson.cn>
Thu, 9 Apr 2026 10:56:38 +0000 (18:56 +0800)
Extend the PMU test suite to cover overflow interrupts. The test enables
the PMI (Performance Monitor Interrupt), sets counter 0 to one less than
the overflow value, and verifies that an interrupt is raised when the
counter overflows. A guest interrupt handler checks the interrupt cause
and disables further PMU interrupts upon success.

Signed-off-by: Song Gao <gaosong@loongson.cn>
Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
tools/testing/selftests/kvm/include/loongarch/pmu.h
tools/testing/selftests/kvm/include/loongarch/processor.h
tools/testing/selftests/kvm/loongarch/pmu_test.c

index 2f734a1d1ae41c29ef400a6185876919170f8aa5..478e6a9bbb2b4301809ad7bf2e5a0e4c98d8ee17 100644 (file)
@@ -29,6 +29,7 @@
 #define  CSR_PERFCTRL_PLV1                     BIT(17)
 #define  CSR_PERFCTRL_PLV2                     BIT(18)
 #define  CSR_PERFCTRL_PLV3                     BIT(19)
+#define  CSR_PERFCTRL_PMIE                     BIT(20)
 #define PMU_ENVENT_ENABLED     (CSR_PERFCTRL_PLV0 | CSR_PERFCTRL_PLV1 | CSR_PERFCTRL_PLV2 | CSR_PERFCTRL_PLV3)
 
 /* Hardware event codes (from LoongArch perf_event.c */
 #define EXPECTED_CYCLES_MIN                     NUM_LOOPS       /* At least 1 cycle per iteration */
 #define UPPER_BOUND                            (10 * NUM_LOOPS)
 
+#define PMU_OVERFLOW                           (1ULL << 63)
+
+static inline void pmu_irq_enable(void)
+{
+       unsigned long val;
+
+       val = csr_read(LOONGARCH_CSR_ECFG);
+       val |= ECFGF_PMU;
+       csr_write(val, LOONGARCH_CSR_ECFG);
+}
+
+static inline void pmu_irq_disable(void)
+{
+       unsigned long val;
+
+       val = csr_read(LOONGARCH_CSR_ECFG);
+       val &= ~ECFGF_PMU;
+       csr_write(val, LOONGARCH_CSR_ECFG);
+}
+
 #endif
index 916426707c8677a791ef1540ffd531163748f654..93dc1fbd2e79d86770466b0cf7bb79693d6bcee4 100644 (file)
@@ -83,6 +83,8 @@
 #define LOONGARCH_CSR_PRMD             0x1
 #define LOONGARCH_CSR_EUEN             0x2
 #define LOONGARCH_CSR_ECFG             0x4
+#define  ECFGB_PMU                     10
+#define  ECFGF_PMU                     (BIT_ULL(ECFGB_PMU))
 #define  ECFGB_TIMER                   11
 #define  ECFGF_TIMER                   (BIT_ULL(ECFGB_TIMER))
 #define LOONGARCH_CSR_ESTAT            0x5  /* Exception status */
@@ -90,6 +92,7 @@
 #define  CSR_ESTAT_EXC_WIDTH           6
 #define  CSR_ESTAT_EXC                 (0x3f << CSR_ESTAT_EXC_SHIFT)
 #define    EXCCODE_INT                 0    /* Interrupt */
+#define             INT_PMI                    10   /* PMU interrupt */
 #define      INT_TI                    11   /* Timer interrupt*/
 #define LOONGARCH_CSR_ERA              0x6  /* ERA */
 #define LOONGARCH_CSR_BADV             0x7  /* Bad virtual address */
index c0f25976b160d287f00370dcd6bc62b07649719e..88bb530e336e198723f72eba9b6a83b32413ff43 100644 (file)
@@ -10,6 +10,8 @@
 #include "pmu.h"
 #include "loongarch/processor.h"
 
+static int pmu_irq_count;
+
 /* Check PMU support */
 static bool has_pmu_support(void)
 {
@@ -99,10 +101,41 @@ static void guest_pmu_base_test(void)
        GUEST_ASSERT(cnt[3] > 0 && cnt[3] < UPPER_BOUND);
 }
 
+static void guest_irq_handler(struct ex_regs *regs)
+{
+       unsigned int intid;
+
+       pmu_irq_disable();
+       intid = !!(regs->estat & BIT(INT_PMI));
+       GUEST_ASSERT_EQ(intid, 1);
+       GUEST_PRINTF("Get PMU interrupt\n");
+       WRITE_ONCE(pmu_irq_count, pmu_irq_count + 1);
+}
+
+static void guest_pmu_interrupt_test(void)
+{
+       uint64_t cnt;
+
+       csr_write(PMU_OVERFLOW - 1, LOONGARCH_CSR_PERFCNTR0);
+       csr_write(PMU_ENVENT_ENABLED | CSR_PERFCTRL_PMIE | LOONGARCH_PMU_EVENT_CYCLES, LOONGARCH_CSR_PERFCTRL0);
+
+       cpu_relax();
+
+       GUEST_ASSERT_EQ(pmu_irq_count, 1);
+       cnt = csr_read(LOONGARCH_CSR_PERFCNTR0);
+       GUEST_PRINTF("csr_perfcntr0 is %lx\n", cnt);
+       GUEST_PRINTF("PMU interrupt test success\n");
+
+}
+
 static void guest_code(void)
 {
        guest_pmu_base_test();
 
+       pmu_irq_enable();
+       local_irq_enable();
+       guest_pmu_interrupt_test();
+
        GUEST_DONE();
 }
 
@@ -128,8 +161,11 @@ int main(int argc, char *argv[])
        vm = vm_create(VM_MODE_P47V47_16K);
        vcpu = vm_vcpu_add(vm, 0, guest_code);
 
+       pmu_irq_count = 0;
        vm_init_descriptor_tables(vm);
        loongarch_vcpu_setup(vcpu);
+       vm_install_exception_handler(vm, EXCCODE_INT, guest_irq_handler);
+       sync_global_to_guest(vm, pmu_irq_count);
 
        attr.group = KVM_LOONGARCH_VM_FEAT_CTRL,
        attr.attr = KVM_LOONGARCH_VM_FEAT_PMU,