linux对中断向量的处理

a.异常向量入口:(linux-4.19-rc3\arch\arm\kernel\entry-armv.S)

对于ARM处理器而言,当发生异常的时候,处理器会暂停当前指令的执行,保存现场,转而去执行对应的异常向量处的指令,当处理完该异常的时候,恢复现场,回到原来的那点去继续执行程序。系统所有的异常向量(共计8个)组成了异常向量表。向量表(vector table)的代码如下:

    .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

异常向量表可能被安放在两个位置上:

(1)异常向量表位于0x0的地址。这种设置叫做Normal vectors或者Low vectors。

(2)异常向量表位于0xffff0000的地址。这种设置叫做high vectors

具体是low vectors还是high vectors是由ARM的一个叫做的SCTLR寄存器的第13个bit (vector bit)控制的。对于启用MMU的ARM Linux而言,系统使用了high vectors
为什么不用low vector呢?对于linux而言,0~3G的空间是用户空间,如果使用low vector,那么异常向量表在0地址,那么则是用户空间的位置,因此linux选用high vector。当然,使用Low vector也可以,这样Low vector所在的空间则属于kernel space了。(也就是说,3G~4G的空间加上Low vector所占的空间属于kernel space)
为了防止这块区域被访问到,vector table所在的空间通常被设置为read only。

b.中断向量: vector_irq

/*
 * Interrupt dispatcher
 */
    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

vector_stub宏的定义如下:

.macro	vector_stub, name, mode, correction=0
	.align	5

vector_\name:
	.if \correction
	sub	lr, lr, #\correction
	.endif

	@
	@ Save r0, lr_<exception> (parent PC) and spsr_<exception>
	@ (parent CPSR)
	@
	stmia	sp, {r0, lr}		@ save r0, lr
	mrs	lr, spsr
	str	lr, [sp, #8]		@ save spsr

	@
	@ Prepare for SVC32 mode.  IRQs remain disabled.
	@
	mrs	r0, cpsr
	eor	r0, r0, #(\mode ^ SVC_MODE | PSR_ISETSTATE)
	msr	spsr_cxsf, r0 

	@
	@ the branch table must immediately follow this code
	@
	and	lr, lr, #0x0f
 THUMB(	adr	r0, 1f			)
 THUMB(	ldr	lr, [r0, lr, lsl #2]	)
	mov	r0, sp
 ARM(	ldr	lr, [pc, lr, lsl #2]	)
	movs	pc, lr			@ branch to handler in SVC mode
ENDPROC(vector_\name)

	.align	2
	@ handler addresses follow this label
1:
	.endm

展开后得到

/*
 * Interrupt dispatcher
 */
    .align    5

vector_irq:
    sub    lr, lr, #4          @得到irq返回地址

    @
    @ Save r0, lr_<exception> (parent PC) and spsr_<exception>
    @ (parent CPSR)
    @
    stmia    sp, {r0, lr}      @ r0写入sp地址 sp+=4 lr写入sp地址 sp+=4
    mrs    lr, spsr            @ spsr取到lr
    str    lr, [sp, #8]        @ 将spsr写入sp+8的地址

    @
    @ Prepare for SVC32 mode.  IRQs remain disabled.
    @
    mrs    r0, cpsr             @取得cpsr
    eor    r0, r0, #(IRQ_MODE  ^ SVC_MODE) @IRQ_MODE =  0x00000012 SVC_MODE = 0x00000013 两者异或得到0x01 将r0的的0x01位取反
    msr    spsr_cxsf, r0        @将r0重新写入spsr,设置为svc模式

    @
    @ the branch table must immediately follow this code
    @
    @lr的内容为spsr,spsr是发生IRQ时保存下来的CPSR 实际上就是根据CPSR.M[3:0]的值进行跳转
    and    lr, lr, #0x0f        @获取spsr低4位(模式位),发生在用户空间为0,发生在内核空间为3
    mov    r0, sp
    ldr    lr, [pc, lr, lsl #2] @lr = pc + lr * 4 要么是__irq_usr的地址 ,要么是__irq_svc的地址
    movs   pc, lr               @ 带s后缀的指令会把spsr赋值给cpsr 此时会进入管理模式 然后pc=lr 跳转执行对应函数


    .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

 

最终实现的功能如下:
计算处理完异常的返回地址;
保存寄存器(r0,lr,spsr)
进入管理模式;
最后根据进入异常前的模式跳转到相应的某个分支。
关于spsr和cpsr的描述可以看看这篇博文https://www.cnblogs.com/lifexy/p/7101686.html

 

c. __irq_usr/__irq_svc
这2个函数的处理过程类似:
保存现场
调用 irq_handler
恢复现场

__irq_usr: //下面的语句分别是一个个宏定义 
	usr_entry
	kuser_cmpxchg_check
	irq_handler
	get_thread_info tsk
	mov	why, #0
	b	ret_to_user_from_irq
 UNWIND(.fnend		)
ENDPROC(__irq_usr)
__irq_svc:
	svc_entry
	irq_handler

#ifdef CONFIG_PREEMPT
	ldr	r8, [tsk, #TI_PREEMPT]		@ get preempt count
	ldr	r0, [tsk, #TI_FLAGS]		@ get flags
	teq	r8, #0				@ if preempt count != 0
	movne	r0, #0				@ force flags to 0
	tst	r0, #_TIF_NEED_RESCHED
	blne	svc_preempt
#endif

	svc_exit r5, irq = 1			@ return from exception
 UNWIND(.fnend		)
ENDPROC(__irq_svc)

d.irq_handler: 将会调用C函数 handle_arch_irq

.macro  irq_handler
#ifdef CONFIG_GENERIC_IRQ_MULTI_HANDLER
    ldr r1, =handle_arch_irq @伪指令 得到handle_arch_irq函数的绝对地址
    mov r0, sp
    @设置返回地址 badr是一个宏定义在/arch/arm/include/asm/assembler.h中 
    badr    lr, 9997f       
    ldr pc, [r1]             @赋值pc 跳转执行
#else
    arch_irq_handler_default
#endif
9997:
    .endm

搜索handle_arch_irq可以知道,实际上是set_handle_irq()设置了这个函数指针。

int __init set_handle_irq(void (*handle_irq)(struct pt_regs *))
{
	if (handle_arch_irq)
		return -EBUSY;

	handle_arch_irq = handle_irq;
	return 0;
}

对于不同的芯片,会调用这个函数设置不同的handle_arch_irq。

对于S3C2440来说, drivers/irqchip/irq-s3c24xx.c中的s3c24xx_init_intc()函数调用了set_handle_irq()

static struct s3c_irq_intc * __init s3c24xx_init_intc(struct device_node *np,
				       struct s3c_irq_data *irq_data,
				       struct s3c_irq_intc *parent,
				       unsigned long address)
{
	.....

	set_handle_irq(s3c24xx_handle_irq);

    .....
}

s3c24xx_handle_irq()是用于处理中断的C语言入口函数,也是中断的总入口

asmlinkage void __exception_irq_entry s3c24xx_handle_irq(struct pt_regs *regs)
{
	do {
		if (likely(s3c_intc[0]))
			if (s3c24xx_handle_intc(s3c_intc[0], regs, 0))
				continue;

		if (s3c_intc[2])
			if (s3c24xx_handle_intc(s3c_intc[2], regs, 64))
				continue;

		break;
	} while (1);
}

e. handle_arch_irq的处理过程:

1.读 int controller, 得到hwirq(硬件中断号)

2.根据hwirq得到virq(虚拟中断号,虚拟中断号一般是硬件中断号的偏移)

3.调用 irq_desc[virq].handle_irq

 

如果该中断没有子中断, irq_desc[virq].handle_irq的操作:

1. 调用irq_desc[virq].handle_irq函数

2. 取出irq_desc[virq].action链表中的每个节点的handler, 执行它

3. 使用irq_desc[virq].irq_data.chip的函数清中断

 

如果该中断是由子中断产生, irq_desc[virq].handle_irq的操作:

1. 读 sub int controller, 得到hwirq'

2. 根据hwirq'得到virq

3. 调用 irq_desc[virq].handle_irq,之后的流程等同于没有子中断的操作

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值
>