커널 모듈에서 ARM 범용 타이머용 인터럽트 핸들러를 등록하는 방법은 무엇입니까?

커널 모듈에서 ARM 범용 타이머용 인터럽트 핸들러를 등록하는 방법은 무엇입니까?

현재 Raspberry Pi 5의 범용 타이머 타이머 중 하나에 대한 사용자 지정 처리기를 등록하려고 하는데 안타깝게도 작동하지 않습니다.

지금까지 내가 한 일은 장치 트리의 타이머 항목을 보는 것뿐이며 언급된 4개의 인터럽트, PPI 10, 11, 13 및 14가 있습니다.

timer {
        compatible = "arm,armv8-timer";
        interrupts = <GIC_PPI 13 (GIC_CPU_MASK_SIMPLE(4) |
                      IRQ_TYPE_LEVEL_LOW)>,
                 <GIC_PPI 14 (GIC_CPU_MASK_SIMPLE(4) |
                      IRQ_TYPE_LEVEL_LOW)>,
                 <GIC_PPI 11 (GIC_CPU_MASK_SIMPLE(4) |
                      IRQ_TYPE_LEVEL_LOW)>,
                 <GIC_PPI 10 (GIC_CPU_MASK_SIMPLE(4) |
                      IRQ_TYPE_LEVEL_LOW)>;
        /* This only applies to the ARMv7 stub */
        arm,cpu-registers-not-fw-configured;
    };

이러한 인터럽트를 살펴보면 /proc/interrupts10에는 핸들러가 설치되지 않은 것 같습니다.

           CPU0       CPU1       CPU2       CPU3       
  9:          0          0          0          0     GICv2  25 Level     vgic
 11:          0          0          0          0     GICv2  30 Level     kvm guest ptimer
 12:          0          0          0          0     GICv2  27 Level     kvm guest vtimer
 13:       4018       9245       1668       9893     GICv2  26 Level     arch_timer
 14:       1147          0          0          0     GICv2  65 Level     107c013880.mailbox
 15:          5          0          0          0     GICv2 153 Level     uart-pl011
...

(또한 장치 트리에 언급되지 않은 경우 "kvm guest vtimer"가 어디서 나오는지 알고 싶습니다. 아는 사람 있나요?)

이 지식을 바탕으로 이제 다음과 같은 간단한 커널 모듈을 연결합니다.

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/interrupt.h>

MODULE_LICENSE("GPL");

DEFINE_PER_CPU(int, dev_id);

irqreturn_t test_handler(int irq, void *dev_id)
{
        int this_cpu = smp_processor_id();

        printk("CPU %i received interrupt!\n", this_cpu);

        return IRQ_HANDLED;
}

int test_init(void)
{
        u64 ctl = 0, cval = 0, tval = 0;
        int err = 0, cpu = 0;

        cpu = get_cpu();
        printk("CPU: %i\n", cpu);

        err = request_percpu_irq(10, test_handler, "test_module", &dev_id);
        printk("request_percpu_irq() err: %i\n", err);

        ctl = read_sysreg(CNTHP_CTL_EL2);
        cval = read_sysreg(CNTHP_CVAL_EL2);
        tval = read_sysreg(CNTHP_TVAL_EL2);
        printk("CNTHP_CTL_EL2: %llu\n", ctl);
        printk("CNTHP_CVAL_EL2: %llu\n", cval);
        printk("CNTHP_TVAL_EL2: %llu\n", tval);

        write_sysreg(1000, CNTHP_TVAL_EL2);

        cval = read_sysreg(CNTHP_CVAL_EL2);
        tval = read_sysreg(CNTHP_TVAL_EL2);
        printk("CNTHP_CVAL_EL2: %llu\n", cval);
        printk("CNTHP_TVAL_EL2: %llu\n", tval);

        put_cpu();

        return 0;
}

void test_exit(void)
{
        free_percpu_irq(10, &dev_id);
}

module_init(test_init);
module_exit(test_exit);

다시 살펴보면 /proc/interrupts내 핸들러가 올바르게 등록된 것으로 보입니다.

           CPU0       CPU1       CPU2       CPU3       
  9:          0          0          0          0     GICv2  25 Level     vgic
 10:          0          0          0          0     GICv2  29 Level     test_module
 11:          0          0          0          0     GICv2  30 Level     kvm guest ptimer
 12:          0          0          0          0     GICv2  27 Level     kvm guest vtimer
 13:       2597       1798       3172       1910     GICv2  26 Level     arch_timer
 14:        249          0          0          0     GICv2  65 Level     107c013880.mailbox
 15:          5          0          0          0     GICv2 153 Level     uart-pl011
...

하지만 코드에서 내가 하려는 작업은 인터럽트를 생성하여 새 핸들러를 트리거하는 것이지만 안타깝게도 이 방법은 작동하지 않습니다. 일반 타이머 참조에 따르면 인터럽트 번호 10(PPI 오프셋 16을 추가하는 경우 26)은 접두사 및 접미사 Non-secure EL2 Physical Timer가 있는 관련 시스템 레지스터 와 연결되어야 합니다 .CNTHPEL2

다음은 시스템 레지스터를 통해 타이머 구성이 작동했음을 나타내는 관련 dmesg 항목입니다. 다음 조건에 따라 인터럽트 생성도 활성화되어야 합니다 CNTHP_CTL_EL2.

[   26.007926] test: loading out-of-tree module taints kernel.
[   26.008154] CPU: 3
[   26.008167] request_percpu_irq() err: 0
[   26.008169] CNTHP_CTL_EL2: 1
[   26.008172] CNTHP_CVAL_EL2: 1409277728
[   26.008174] CNTHP_TVAL_EL2: 185726
[   26.008177] CNTHP_CVAL_EL2: 1409093405
[   26.008179] CNTHP_TVAL_EL2: 998

하지만 내부의 인쇄물이 test_handler()손실되어 인터럽트가 내 핸들러에 도달하지 못하는 것 같습니다. 불행하게도 제가 한동안 갇혀 있던 곳이 바로 여기입니다.

그렇다면 Linux 커널 모듈에서 ARM 범용 타이머에 대한 인터럽트 핸들러를 등록하는 방법은 무엇입니까? (왜 내 뜻대로 안 되는 걸까?)

관련 정보