현재 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
...
하지만 코드에서 내가 하려는 작업은 인터럽트를 생성하여 새 핸들러를 트리거하는 것이지만 안타깝게도 이 방법은 작동하지 않습니다. 일반 타이머 참조에 따르면 인터럽트 번호 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 범용 타이머에 대한 인터럽트 핸들러를 등록하는 방법은 무엇입니까? (왜 내 뜻대로 안 되는 걸까?)