微波EDA网,见证研发工程师的成长!
首页 > 硬件设计 > 嵌入式设计 > arm linux 下中断流程简要分析中断处理流程

arm linux 下中断流程简要分析中断处理流程

时间:11-09 来源:互联网 点击:
三 响应中断

首先在分析源码之前,让我们了解一些原理性的东西,我们都知道在处理中断要保存当前现场状态,然后才能处理中断,处理完之后还要把现场状态恢复过来才能返回到被中断的地方继续执行,这里要说明的是在指令跳转到中断向量的地方开始执行之前,CPU帮我们做了哪些事情:


R14_irq =要执行的下条指令地址+ 4//这里的下条指令是相对于被中断指令的下条。即返回地址

SPSR_irq = CPSR//保存的现场状态,r0到r12要由我们软件来保存(如果需要的话)。

CPSR[4:0] = 0b10010//进入中断模式

CPSR[5] = 0//在ARM模式下执行(不是Thumb下)

CPSR[7] = 1//关掉IRQ中断,FIQ还是开着

PC = 0Xffff0018/0x00000018//根据异常向量表的位置,跳转到特定的中断向量处去执行。

更详细的关于异常处理的细节可参考<>

接下来我们在来分析watchdog产生中断后的处理流程:

当watchdog超时时将会产生中断,中断号就是IRQ_WDT,当产生中断时,系统将从跳转表中的中断位置开始运行,对于我们这篇文章来说:是从0xffff0000 + 24处开始运行。 这个地址的指令是:

bvector_irq + stubs_offset

即直接跳转到vector_irq处去运行。这些都在中断初始化的时候分析过了。

我们来看vector_irq,它是通过宏vector_stub来定义的:

arch/arm/kernel/entry-armv.S:

/*

* Interrupt dispatcher

*/

vector_stubirq, IRQ_MODE, 4/*这是个宏定义*/

/*下面这些都是不同模式下的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)

.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

arch/arm/kernel/entry-armv.S:

.macrovector_stub, name, mode, correction=0

.align5

vector_/name:

.if /correction

sublr, lr, #/correction

.endif

@

@ Save r0, lr_ (parent PC) and spsr_

@ (parent CPSR)

@

stmiasp, {r0, lr}@ save r0, lr

mrslr, spsr

strlr, [sp, #8]@ save spsr

@

@ Prepare for SVC32 mode.IRQs remain disabled.

@

mrsr0, cpsr

eorr0, r0, #(/mode ^ SVC_MODE)

msrspsr_cxsf, r0

@

@ the branch table must immediately follow this code

@

andlr, lr, #0x0f

movr0, sp

ldrlr, [pc, lr, lsl #2]

movspc, lr@ branch to handler in SVC mode

.endm

这样展开后vector_irq如下所示:

arch/arm/kernel/entry-armv.S:

vector_irq:

.if 4

@ lr保存的是被打断指令处地址+8的值,(看上面的分析,由PC得到), 这里-4则就是中断

@处理完后的返回地址,在中断处理完后该值会赋给PC

sublr, lr, #4

.endif

@

@ Save r0, lr_ (parent PC) and spsr_

@ (parent CPSR)

@ r0后面会用到所以要保存。

stmiasp, {r0, lr}@ save r0, lr,保存r0,lr到栈上,这里的栈是中断模式下的。

mrslr, spsr@获取spsr的值,该值保存了被中断处执行环境的状态(参考上面的分析)

strlr, [sp, #8]@ save spsr, 保存到栈上

@

@ Prepare for SVC32 mode.IRQs remain disabled.

@

mrsr0, cpsr

eorr0, r0, #( IRQ_MODE ^ SVC_MODE)

msrspsr_cxsf, r0@把spsr设置成管理模式

@

@ the branch table must immediately follow this code

@

andlr, lr, #0x0f

movr0, sp

ldrlr, [pc, lr, lsl #2]

movspc, lr@ branch to handler in SVC mode @ pc = lr, cpsr = spsr

.endm

movs的目的对象如果是pc的话,则还会把spsr赋值给cpsr,上面我们看到spsr被设成管理模式,因此这条语句过后的代码也就跑在了管理模式下。

此时的栈情况如下:



S_FRAME_SIZE,S_PC在arch/arm/kernel/Asm-offsets.c:中定义

DEFINE(S_FRAME_SIZE,sizeof(struct pt_regs));

DEFINE(S_PC,offsetof(struct pt_regs, ARM_pc));

include/asm-arm/Ptrace.h:

struct pt_regs {

long uregs[18];

};

#define ARM_pcuregs[15]

,pt_regs中对应的就是上面栈上的18个寄存器,ARM_pc是pc寄存器存放在这个数组中的偏移。

接着看get_thread_info, 它也是个宏,用来获取当前线程的地址。在我的一篇linux启动代码分析里曾写过线程的定义方式:

include/linux/Sched.h:

union thread_union {

struct thread_info thread_info;/*线程属性*/

unsigned long stack[THREAD_SIZE/sizeof(long)];/*栈*/

};

由它定义的线程是8K字节对齐的, 并且在这8K的最低地址处存放的就

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

网站地图

Top