我目前正在尝试为 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/interrupts
这些中断,10 似乎还没有安装处理程序:
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
...
但我还在代码中尝试做的是生成一个中断来触发我的新处理程序,但不幸的是这不起作用。根据 Generic Timer 参考,中断号 10(如果添加 PPI 偏移量 16,则为 26)应与 关联Non-secure EL2 Physical Timer
,关联的系统寄存器具有前缀CNTHP
和后缀EL2
。
以下是相关的 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 通用定时器注册中断处理程序呢? (为什么它不按我的方式工作?)