カーネルモジュールでARMユニバーサルタイマの割り込みハンドラを登録するには?

カーネルモジュールでARMユニバーサルタイマの割り込みハンドラを登録するには?

現在、Raspberry Pi 5の汎用タイマータイマーの1つにカスタムハンドラーを登録しようとしていますが、残念ながら機能しません。

これまでに私がしたことは、デバイスツリーのタイマーエントリを見ることだけであり、上記の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ユニバーサルタイマーの割り込みハンドラを登録する方法は? (なぜ私の思い通りにならないのか?)

関連情報