Armor的中断向量表放在内存的什么位置?

中断向量表放在arch/arm/kernel/entry-armv.S这个文件里

__vectors_start:
#swi SYS_ERROR0
b vector_addrexcptn + stubs_offset
b vector_und + stubs_offset
ldr pc, .LCvswi + stubs_offset
b vector_pabt + stubs_offset
b vector_dabt + stubs_offset
b vector_addrexcptn + stubs_offset
b vector_irq + stubs_offset
b vector_fiq + stubs_offset

.globl __vectors_end
__vectors_end:

在arch/arm/traps.c中

     memcpy((void *)0xffff0000, __vectors_start, __vectors_end - __vectors_start);   //中断向量表被拷贝到0xffff0000处,硬件规定的位置
memcpy((void *)0xffff0200, __stubs_start, __stubs_end - __stubs_start);

硬件发生中断后怎样找到中断入口函数的?

从上面的中断向量表中可 以看到,发生中断时系统会跳到 vector_irq + stubs_offset处运行,这个位置实际上就是中断入口函数。vector_irq已经是中断的入口函数了,为什么又要加上 stubs_offset?是因为b指令实际上是相对相前PC的跳转,也就是说当汇编器看到B指令后会把要跳转的标签转化为相对于当前PC的偏移量写入指 令码。从上面的代码可以看到中断向量表和stubs都发生了代码搬移,所以如果中断向量表中仍然写成b vector_irq,那么实际执行的时候就无法跳转到搬移后的vector_irq处,因为指令码里写的是原来的偏移量,所以需要把指令码中的偏移量写 成搬移后的。我们把搬移前的中断向量表中的irq入口地址记irq_PC,它在中断向量表的偏移量就是irq_PC-vectors_start, vector_irq在stubs中的偏移量是vector_irq-stubs_start,这两个偏移量在搬移前后是不变的。搬移后 vectors_start在0xffff0000处,而stubs_start在0xffff0200处,所以搬移后的vector_irq相对于中断 向量中的中断入口地址的偏移量就是,200+vector_irq在stubs中的偏移量再减去中断入口在向量表中的偏移量,即200+ vector_irq-stubs_start-irq_PC+vectors_start = (vector_irq-irq_PC) + vectors_start+200-stubs_start,对于括号内的值实际上就是中断向量表中写的vector_irq,减去irq_PC是由汇 编器完成的,而后面的 vectors_start+200-stubs_start就应该是stubs_offset,实际上在entry-armv.S中也是这样定义的

进入中断后在irq模式完成什么工作?

vector_irq是通过宏来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)
msr spsr_cxsf, r0

@
@ the branch table must immediately follow this code
@
and lr, lr, #0x0f
mov r0, sp
ldr lr, [pc, lr, lsl #2]
movs pc, lr @ branch to handler in SVC mode
.endm

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
......

从 上面这段代码可以看出,vector_irq把发生中断时的r0,PC-4以及CPSR压栈(注意,压入的的irq模式的堆栈),把中断栈的栈指针赋给 r0,最后根据原来发生中断时CPU所处的工作模式(CPSR的低4位)找到相应的入口函数,在进入svc模式后进一步处理中断。

__irq_usr完成的工作是什么?

它主要通过调用宏usr_entry进一步保存现场,然后调用irq_handler进行中断处理,保存的栈结构如下:

-1

CPSR

PC-4

LR

SP

R12

...

R2

R1

R0

其中的LR,SP是USR模式下的,R0,CPSR,PC-4是从中断栈中拷贝的

irq_handler的作用是什么?

  • 它首先通过宏 get_irqnr_and_base获得中断号,存在r0,然后把上面建立的pt_regs结构的指针,也就是sp值赋给r1,把调用宏 get_irqnr_and_base的位置作为返回地址(为了处理下一个中断???)然后调用 asm_do_IRQ进一步处理中断,以上这些操作都在建立在获得中断号的前提下,也就是有中断发生
  • 代码实现

.macro  irq_handler
1: get_irqnr_and_base r0, r6, r5, lr
movne r1, sp
@
@ routine called with r0 = irq number, r1 = struct pt_regs *
@
adrne lr, 1b
bne asm_do_IRQ

.endm

具体的中断处理会在asm_do_IRQ根据中断号调用相应的中断处理程序完成。

http://hi.baidu.com/wudx05/blog/item/5314935c834f4e41fbf2c0dc.html

 

浅析armlinux 2.4.19中断irq分发例程的派发流程之中间层
本文来自: (www.91linux.com) 详细出处参考:http://www.91linux.com/html/article/qianrushiyingyong/20080303/9817.html

接续前一篇《浅析armlinux 2.4.19中断irq分发例程的派发流程之根基》
看看irq寄存器环境保存和高层asm_do_IRQ()中断处理函数的调用跳转[gliethttp_20071225].

//----------------------------------------------------------------------
//1.先看看__irq_svc中断irq处理例程的实现代码'
//在arch\arm\kernel\entry-header.S中有如下3个常量定义
/*
#ifdef CONFIG_CPU_32
#define S_FRAME_SIZE 72
#define S_OLD_R0 68
#define S_PSR 64
#endif
其实这就是pt_regs结构体的偏移值,该结构体定义在include\asm-arm\proc-armv\ptrace.h中
struct pt_regs {
long uregs[18];
};

#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_r8 uregs[8]
#define ARM_r7 uregs[7]
#define ARM_r6 uregs[6]
#define ARM_r5 uregs[5]
#define ARM_r4 uregs[4]
#define ARM_r3 uregs[3]
#define ARM_r2 uregs[2]
#define ARM_r1 uregs[1]
#define ARM_r0 uregs[0]
#define ARM_ORIG_r0 uregs[17]
*/
.align 5
//因为在vector_IRQ中irq模式下的lr_irq和lr_spsr执行msr spsr_c, r13之后,使用movs pc, lr进行跳
//转到__irq_svc,所以带s的操作指令movs,将使spsr_c的状态自动恢复到cpsr中,也就是使cpu工作在
//禁止irq中断的svc模式下[当然irq的I位在发生irq的同时就有硬件自动禁用了gliethttp_20071225]
//所以linux2.4内核下irq处理程序使用的sp就是暂时借用了svc内核空间程序的堆栈,如下代码也工作在svc模式
__irq_svc:sub sp, sp, #S_FRAME_SIZE
//因为我的at91rm9200在linux2.4.19中工作在"小端"模式,低址存低位,
//所以下面的r1=sp-S_FRAME_SIZE的地址就是&uregs[0]的地址[gliethttp_20071225]
//从低址向高址依次存放r0,r1,...,r12,也就是将数据存放到结构体的uregs[0]~uregs[12]中
stmia sp, {r0 - r12}
//参看《浅析armlinux 2.4.19中断irq分发例程的派发流程之根基》
//.LCirq+0 存放了 lr-4的地址值,也就是irq中断处理函数处理完成之后,返回到的pc地址值
//.LCirq+4 存放了 spsr,也就是irq中断发生跳转到irq处理函数vector_IRQ之前cpu所处的模式值
// 主要是用来对I和F位以及几个N、Z、C和V标志位的恢复
ldr r7, .LCirq
add r5, sp, #S_FRAME_SIZE//r5存放了由该irq中断处理程序借用的svc的实际堆栈指针
ldmia r7, {r7 - r9}//r7=lr-4;r8=spsr;r9=随意值,实际为__temp_abt数值
add r4, sp, #S_SP//r4=&ARM_sp=&uregs[13]地址
//r6=当前svc模式下的lr,不允许嵌套中断发生;
//因为这是svc内核代码正在执行的时候发生的irq中断处理,所以当然要保存svc模式下正在运算的lr地址值,
//这样从irq返回时也才能够继续执行svc模式下被irq中断了的运行的内核代码[gliethttp_20071225]
mov r6, lr
//gliethttp_20071225
//按照存pt_regs结构体的储空间规则存放r0-r12,sp_svc,lr_svc,pc,cpsr,ARM_ORIG_r0;总之pt_regs存放了
//发生irq中断之前cpu所运行的整个环境,比如:正在内核空间进行pte_alloc()的操作的时候,irq发生到此,
//那么pt_regs存放着pte_alloc()运行到被irq中断的那个时刻,所有的寄存器值[gliethttp_20071225].
stmia r4, {r5, r6, r7, r8, r9}
/*
//at91rm9200中断号读取代码
.macro get_irqnr_and_base, irqnr, irqstat, base, tmp
ldr \base, =(AT91C_VA_BASE_SYS)
ldr \irqnr, [\base, #AIC_IVR]
ldr \irqstat, [\base, #AIC_ISR]
teq \irqstat, #0
streq \tmp, [\base, #AIC_EOICR]
.endm
*/
1: get_irqnr_and_base r0, r6, r5, lr
movne r1, sp
/*
//gliethttp_20071225[entry-header.S]
//Like adr, but force SVC mode (if required)
.macro adrsvc, cond, reg, label
adr\cond \reg, \label
.endm
adrsvc ne, lr, 1b等价于subne lr, pc, #32
*/
adrsvc ne, lr, 1b
//gliethttp_20071225
//arch\arm\kernel\irq.c->asmlinkage void asm_do_IRQ(int irq, struct pt_regs *regs)
//对asm_do_IRQ的理解将在另一篇中《浅析armlinux 2.4.19中断irq分发例程的派发流程之高端层》浅析
bne asm_do_IRQ//在I位禁用irq中断期间执行asm_do_IRQ高端处理函数
ldr r0, [sp, #S_PSR]//将发生这个irq中断之前的cpu状态寄存器spsr拿出来
msr spsr, r0//拷贝到svc模式的spsr中
//弹出pt_regs结构体中的所有寄存器数值,恢复到被irq中断的内核代码执行处继续执行[gliethttp_20071225]
//^号操作中,如果有pc寄存器,那么和s操作一样,将恢复spsr中的内容到cpsr,同时自动切换cpu模式到spsr
ldmia sp, {r0 - pc}^
//----------------------------------------------------------------------
//2.再看看__irq_usr中断irq处理例程的实现代码
//从用户空间发生的irq中断,
//因为在vector_IRQ中irq模式下的lr_irq和lr_spsr执行msr spsr_c, r13之后,使用movs pc, lr进行跳
//转到__irq_svc,所以带s的操作指令movs,将使spsr_c的状态自动恢复到cpsr中,也就是使cpu工作在
//禁止irq中断的svc模式下[当然irq的I位在发生irq的同时就有硬件自动禁用了gliethttp_20071225]
//所以linux2.4内核下irq处理程序使用的sp就是暂时借用了svc内核空间程序的堆栈,如下代码也工作在svc模式
.align 5
__irq_usr:sub sp, sp, #S_FRAME_SIZE//理解同__irq_svc
stmia sp, {r0 - r12}//注意usr用户模式下的r0-r12和svc模式下的r0-r12是共用的
//参看《浅析armlinux 2.4.19中断irq分发例程的派发流程之根基》
//.LCirq+0 存放了 lr-4的地址值,也就是irq中断处理函数处理完成之后,返回到的pc地址值
//.LCirq+4 存放了 spsr,也就是irq中断发生跳转到irq处理函数vector_IRQ之前cpu所处的模式值
// 主要是用来对I和F位以及几个N、Z、C和V标志位的恢复
ldr r4, .LCirq
add r8, sp, #S_PC
//r5=用户模式下lr-4的地址值,也就是irq中断处理函数处理完成之后,返回到的pc地址值
//r6=用户模式下的cpsr值,返回时会被使用到
//r7=暂时无用
ldmia r4, {r5 - r7}
stmia r8, {r5 - r7}//uregs[15]=r5;uregs[16]=r6;uregs[17]=r7
stmdb r8, {sp, lr}^//^表示存储user用户模式下的sp,lr寄存器到r8,r8-4处
//通过上面的几个简单操作,被irq中断了的用户空间程序所使用的所有cpu寄存器都被暂时
//保存到了svc内核程序空间的sp堆栈上[gliethttp_20071225]
/*arch\arm\kernel\entry-header.S
.macro alignment_trap, rbase, rtemp, sym
#ifdef CONFIG_ALIGNMENT_TRAP
#define OFF_CR_ALIGNMENT(x) cr_alignment - x
ldr \rtemp, [\rbase, #OFF_CR_ALIGNMENT(\sym)]
mcr p15, 0, \rtemp, c1, c0
#endif
.endm

alignment_trap r4, r7, __temp_irq
zero_fp
对应的反汇编为:
c001737c: e5947024 ldr r7, [r4, #36]
c0017380: ee017f10 mcr 15, 0, r7, cr1, cr0, {0}
c0017384: e3a0b000 mov fp, #0 ; 0x0
*/
//__temp_irq为.LCirq+0的值,也就是lr-4,也就是irq中断处理完成之后,用户空间的pc值
//所以它必须是合法的,必须经过检查
alignment_trap r4, r7, __temp_irq
zero_fp
1: get_irqnr_and_base r0, r6, r5, lr
movne r1, sp
adrsvc ne, lr, 1b
bne asm_do_IRQ//参数r0=irq,r1=regs
mov why, #0
/*
arch\arm\kernel\entry-header.S
.macro get_current_task, rd
mov \rd, sp, lsr #13
mov \rd, \rd, lsl #13
.endm
*/
//因为当前在svc模式下,和当前的usr空间是一一对应的
//在schedule的时候,没有用户模式下的task都会对应一个内核模式堆栈
//所以这个sp就是svc内核模式下的用户task堆栈
//该内核模式堆栈的低8k空间存放了current的进程task_struct内核数据结构体
get_current_task tsk
b ret_to_user
/*
arch\arm\kernel\entry-common.S
ENTRY(ret_to_user)
ret_slow_syscall:
ldr r1, [tsk, #TSK_NEED_RESCHED]
ldr r2, [tsk, #TSK_SIGPENDING]
teq r1, #0 @ need_resched => schedule()
bne reschedule
1: teq r2, #0 @ sigpending => do_signal()
bne __do_signal
restore:
//从当前sp恢复到usr用户空间,继续执行刚才被irq中断了的用户空间程序[gliethttp_20071225]
restore_user_regs
__do_signal:
enable_irq r1
mov r0, #0 @ NULL 'oldset'
mov r1, sp @ 'regs'
mov r2, why @ 'syscall'
bl SYMBOL_NAME(do_signal) @ note the bl above sets lr
disable_irq r1 @ ensure IRQs are disabled
b restore
*/