微波EDA网,见证研发工程师的成长!
首页 > 硬件设计 > 嵌入式设计 > ARM Linux中断机制分析

ARM Linux中断机制分析

时间:11-09 来源:互联网 点击:

C_MODE,其它位保持不变。

@ the branch table must immediately follow this code

and lr, lr, #0x0f//提取发生异常前的处理器模式,这里也就是usr模式

mov r0, sp

ldr lr, [pc, lr, lsl #2]

movs pc, lr


sp是SVC32模式下的堆栈指针,这里将它移到r0中,就可以作为C函数的第一个参数,即C函数中的pt_regs参数。

pc是当前地址+8,也就是本段代码后面紧跟的跳转表的基地址,lr用于在跳转表中索引,lr左移两位等同于*4,因为每个条目是4字节。从usr模式进入irq模式,则lr=pc+4*0,若从svc模式进入irq,则lr=pc+4*3,即__irq_svc的地址,其他地址进入__irq_invalid出错处理,因为不能从其他模式进入irq异常。

假设这里是从usr进入irq,则执行跳转表中的第一条指令。跳转的基准地址为当前pc,因为ARMv4是三级流水线结构的,它总是指向当前指令的下两条指令的地址,尽管以后版本的指令流水线扩展为5级和8级,但是这一特性一直被兼容处理,也即pc(excute)=pc(fetch) + 8,其中:pc(fetch)是当前正在执行的指令,就是之前取该指令时候的PC的值;pc(execute):当前指令执行的,计算中如果用到pc,是指此时pc的值。

当mov指令的目标寄存器是PC,且指令以S结束,则它会把spsr的值恢复给cpsr,上面说到当前的spsr中保存的是r0的值,即svc模式。所以本条指令是跳转到__irq_usr的同时将处理器模式转为svc模式。异常处理一定要进入svc模式的原因是:异常处理一定要进入PL1特权级;另一个原因是使能嵌套中断。具体原因在问题4中解释。关于__irq_svc和__irq_invalid暂时不讨论。

/*

* Interrupt dispatcher以下跳转表必须紧跟在vector_irq之后

*/

vector_stub irq, IRQ_MODE, 4 //生成vector_irq

/*从用户态进入中断的处理函数*/

.long __irq_usr @ 0 (USR_26 / USR_32)

.long __irq_invalid @ 1 (FIQ_26 / FIQ_32)

.long __irq_invalid @ 2 (IRQ_26 / IRQ_32)

/*从SVC进入中断的处理函数*/

.long __irq_svc @ 3 (SVC_26 / SVC_32)

.long __irq_invalid @ 4

.long __irq_invalid @ 5

.long __irq_invalid @ 6

图2IRQ中断处理跳转示意图

注意,以下操作都是在svc模式中,因为要借用SVC模式进行ISP处理,所以需要保存所有SVC模式下的寄存器到SVC堆栈中,最后才去调用中断服务例程(ISP)irq_handler。

2.2.1 __irq_usr

.align 5

__irq_usr:

usr_entry //用于用户模式下发生中断时初始化中断处理堆栈,同时保存所有SVC态寄存器到堆栈。

kuser_cmpxchg_check //对低版本的ARM核来说,用户态无法实现原子比较交换。如果用户态在处理原

//子比较交换的过程中发生中断,需要特殊处理,略过

get_thread_info tsk //根据当前sp指针,将该指针最右边13位清0,获得当前任务的thread_info

#ifdef CONFIG_PREEMPT//如果可以抢占,递增任务的抢占计数

ldr r8, [tsk, #TI_PREEMPT]//T被定义为offsetof(struct thread_info, preempt_count),显然通过tsk就

可以很容易得到进程preempt_count成员的地址了

add r7, r8, #1 @ increment it

str r7, [tsk, #TI_PREEMPT]

#endif

irq_handler //中断服务例程,后面分析

#ifdef CONFIG_PREEMPT

ldr r0, [tsk, #TI_PREEMPT]//获得当前的抢占计数

str r8, [tsk, #TI_PREEMPT]//并将r8中的值保存回去。相当于将前一步递增的抢占计数减回去了

teq r0, r7//r0,r7是调用irq_handler前后的抢占计数,这里进行比较,是防止驱动的ISR

//程序没有配对操作抢占计数导致系统错误。

ARM( strne r0, [r0, -r0] )//如果抢占计数被破坏,则强制写入0.

THUMB( movne r0, #0 )

THUMB( strne r0, [r0] )

#endif

mov why, #0

b ret_to_user //返回到用户态

UNWIND(.fnend )

ENDPROC(__irq_usr)


接下来分别看各个函数的功能

arch/arm/include/asm/ptrace.h

struct pt_regs {

unsigned long uregs[18];

};

#endif /* __KERNEL__ */

#define ARM_cpsr uregs[16]

#define ARM_pc uregs[15]

#define ARM_lr uregs[14]

#define ARM_sp uregs[13]

#define ARM_ip uregs[12]

#define ARM_fp uregs[11]

#define ARM_r10 uregs[10]

#define ARM_r9 uregs[9]

……

#define ARM_ORIG_r0 uregs[17]

pt_regs结构体定义,后面要用到。

.macrousr_entry //usr_entry宏定义

UNWIND(.fnstart )

UNWIND(.cantunwind ) @ dont unwind the user space

sub sp, sp, #S_FRAME_SIZE @ S_FRAME_SIZE定义在trimslice-kernel\arch\arm\kernel\arm-offsets.c中S_FRAME_SIZE被定义为sizeof(struct pt_regs)的大小=18*4=72字节,将svc32堆栈指针向低地址

Copyright © 2017-2020 微波EDA网 版权所有

网站地图

Top