本篇文章是基于linux-4.9.88内核进行分析,使用的单板是imx6ull。
1、linux中断所涉及的各种数据结构关系
首先,我们要明确一点,hwirq与virq并不是一回事,hwirq就是硬件中断号,比如用户按键、串口中断、网络中断等这些,都会在gic通用中断控制器中标识出一个中断号,是属于硬件层面的序号。
为什么不直接使用hwirq呢?对于驱动工程师而言,我们和CPU视角是一样的,我们只希望得到一个IRQ number,而不关系具体是那个interrupt controller上的那个HW interrupt ID。这样一个好处是在中断相关的硬件发生变化的时候,驱动软件不需要修改。因此,linux kernel中的中断子系统需要提供一个将HW interrupt ID映射到IRQ number上来的机制,就是irq_domain结构的作用。
从上图中可以看到,linux中断所涉及到的各个数据结构,主要是通过irq_desc这个结构进行链接
- 用户申请到的每一个中断都会为它分配一个irq_desc结构。
- irq_chip结构主要用来执行中断的各种操作,比如,屏蔽中断和使能中断
- irq_domain结构主要用来保存hwirq和virq的映射关系,其中有两个重要的函数,.xlate和.map。
- irq_desc结构中的handle_irq与action链表中的函数不是同一个函数,处理不同的事件,而用户注册的中断服务函数是action链表中的一个函数。
2、异常向量表的注册
我们知道,当发生某一个中断时,CPU就会跳转到相应的异常入口中去执行异常函数,而中断也是异常的一种,因此,我们有必要分析一下异常向量表的注册。
arch/arm/kernel/entry-armv.S
.section .vectors, "ax", %progbits
.L__vectors_start:
W(b) vector_rst
W(b) vector_und
W(ldr) pc, .L__vectors_start + 0x1000
W(b) vector_pabt
W(b) vector_dabt
W(b) vector_addrexcptn
W(b) vector_irq
W(b) vector_fiq
这些就是各种异常发生时跳转到的函数,其中中断发生会跳转到vector_irq中,下面就去查找vector_irq,结果是找不到的,它是一个宏,
vector_stub irq, IRQ_MODE, 4
.long __irq_usr @ 0 (USR_26 / USR_32)
.long __irq_invalid @ 1 (FIQ_26 / FIQ_32)
.long __irq_invalid @ 2 (IRQ_26 / IRQ_32)
.long __irq_svc @ 3 (SVC_26 / SVC_32)
.long __irq_invalid @ 4
.long __irq_invalid @ 5
.long __irq_invalid @ 6
.long __irq_invalid @ 7
.long __irq_invalid @ 8
.long __irq_invalid @ 9
.long __irq_invalid @ a
.long __irq_invalid @ b
.long __irq_invalid @ c
.long __irq_invalid @ d
.long __irq_invalid @ e
.long __irq_invalid @ f
将 'stub'替换为 'irq'就是我们中断的跳转入口,对于user模式下,我们会跳转到__irq_usr这个位置,相应的svc模式下,将会跳转到__irq_svc的位置去执行,下面搜索__irq_usr
.align 5
__irq_usr:
usr_entry /* 1. 保存现场 */
kuser_cmpxchg_check
irq_handler /* 2. 处理 */
get_thread_info tsk
mov why, #0
b ret_to_user_from_irq /* 3. 恢复现场 */
UNWIND(.fnend )
ENDPROC(__irq_usr)
因此,irq_handler函数来处理中断,继续搜索
.macro irq_handler
#ifdef CONFIG_MULTI_IRQ_HANDLER
ldr r1, =handle_arch_irq
mov r0, sp
badr lr, 9997f
ldr pc, [r1]
#else
arch_irq_handler_default
#endif
最终会执行到handle_arch_irq函数,这是在gic中设置的一个函数。在讲解如何设置该函数之前先看异常向量表是怎么让内核知道的。
// arch\arm\kernel\head.S
1. bl __lookup_processor_type
......
2. bl __