linux 中断(一)

来源:互联网 发布:spark入门例子linux 编辑:程序博客网 时间:2024/05/17 21:14

对于arm的设备,有七个异常中断向量+一个保留向量一共8个。

是按照顺序排列的。每个向量地址对应一个字大小的空间用于跳转到特定的处理函数。

1 复位向量                       0X00000000/0XFFFF0000

2 未定义指令终止向量   0X00000004/0XFFFF0004

3SWI向量                         0X00000008/0XFFFF0008

4预取指令终止向量        0X0000000c/0XFFFF000c

5数据访问终止向量        0X00000010/0XFFFF0010

6保留                                0X00000014/0XFFFF0014

7外部中断向量                0X00000018/0XFFFF0018

8快速中断向量                0X0000001c/0XFFFF001c

地址一般是固定的        

从0X00000000-0x0000001c或者 0XFFFF0000-0XFFFF001C。通过CP15协处理器的寄存器的某个寄存器可以控制。

当然也有可以自定义的。比如树莓派的BCM2835芯片armv7就可以使用CP2来自定义异常中断向量的地址。

当CPU遇到特定的异常的时候就会跳转到对应的地址上,执行指令,比如当前CPU执行的指令是一个未定义的指令,CPU出现异常。就会跳到未定义指令终止向量(0x00000004/0xffff0004)去执行这里的指令。访问数据出现错误的时候就会跳转到5数据访问终止向量 (0X00000010/0XFFFF0010)这里执行。


外部中断就会跳转到 (外部中断向量  0X00000018/0XFFFF0018)。比如插入了一个USB设备,网卡接收到了数据,就会跳转到这里来执行。

7 外部中断向量IRQ 和快速中断向量FIQ的区别是。FIQ一般用于处理耗时比较短的一些中断,因为FIQ有R8-R14寄存器,比IRQ的R13和R14寄存器要多一些,IRQ例程里面可能要先保护R8-R12,执行完中断函数后再恢复,FIQ就不需要,中断如何区分是插入了USB设备还是网卡接收到了数据呢?这就要通过中断号来区分了。后续再看。

这里先看这几个例程的定义。

arm\kernel\entry-armv.S里面定义了这几个向量 。

__vectors_start:W(b)vector_rstW(b)vector_undW(ldr)pc, __vectors_start + 0x1000W(b)vector_pabtW(b)vector_dabtW(b)vector_addrexcptnW(b)vector_irqW(b)vector_fiq

b表示跳转到xxx。

比如W(b) vector_irq

vector_irq的定义是

vector_stub irq, IRQ_MODE, 4

vector_stub是一个宏,可以展开。

在进入这些模式之前,会执行一系列的操作,并且这些操作不可以被打断,可以参看<深度探索嵌入式操作系统>page44-46。

进入irq模式之前 

1将处理器设置成ARM状态和外部中断模式

2把中断后将要执行的第二条指令(参看ARM流水线)的地址放到中断模式下的R14(lr)寄存器中。将CPSR放到外部中断模式的SPSR寄存器中

3禁止外部设备中断

4设置R15位外部中断向量。也就是PC=vector_irq 跳转到这里执行


看一下vector_irq函数执行的操作。展开vector_stub irq, IRQ_MODE, 4以后。

.macrovector_stub, name, mode, correction=0.align5vector_\name:.if \correctionsublr, lr, #\correction correction=4 因为执行当前指令发生中断的时候,PC已经指向了后续第2条指令(参看ARM指令流水线),所以这里lr=lr-4,处理完毕以后。回到当前中断的下一条指令执行,注意这里的LR已经是IRQ模式下的LR寄存器了。.endif@@ Save r0, lr_<exception> (parent PC) and spsr_<exception>@ (parent CPSR)@stmiasp, {r0, lr}@ save r0, lr 保存R0和R14到堆栈(IRQ模式的堆栈)中。保存被中断的下一条指令lr,r0和lr都用于后续的临时操作r0也要保存。mrslr, spsr                将SPSR放到LR中strlr, [sp, #8]@ save spsr 将SPSR放到SP+8的地址里面 地址从低到高分别是R0 LR SPSR(中断前的CPSR)@@ Prepare for SVC32 mode.  IRQs remain disabled. 准备从中断模式切换到管理模式(切换过去以后,SP和LR不一样了)@mrsr0, cpsreorr0, r0, #(\mode ^ SVC_MODE | PSR_ISETSTATE)msrspsr_cxsf, r0 切换CPSR进入SVC模式@@ the branch table must immediately follow this code@andlr, lr, #0x0f   lr=IRQ模式spsr=中断前的CPSR。这里是获取中断之前的模式.取得cpsr的后四位。movr0, sp          让R0指向IRQ中断模式的堆栈指针ldrlr, [pc, lr, lsl #2]根据之前的模式取得将要跳转的地址。movspc, lr@ branch to handler in SVC modeENDPROC(vector_\name)

首先堆栈如下


spsr (中断前的cpsr)

lr

r0 <-------- sp


执行 ldr lr,[pc,lr,lsl#2]时。PC等于 movs pc,lr下面一条指令。也就是此时的PC指向.long __irq_usr

vector_stubirq, 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

此时的lr是cpsr的后四位 ,0-0xf 跳转到下面的@对应的位置上

 ldr lr,[pc,lr,lsl#2] 这句意思是 先把 lr右移两位(也就是乘4,移动一个long的长度)再把PC+lr里面的值赋值给lr。

也就是根据之前的模式跳转到特定的函数位置。如果之前是usr模式0。那么跳转到__irq_usr。如果是3 跳转到__irq_svc。


假设之前是usr模式。那么就跳转到__irq_usr执行

__irq_usr:usr_entrykuser_cmpxchg_checkirq_handlerget_thread_info tskmovwhy, #0bret_to_user_from_irq UNWIND(.fnend)ENDPROC(__irq_usr)

user_entry是一个宏。主要是保护寄存器

.macrousr_entry, trace=1subsp, sp, #S_FRAME_SIZE    预留一部分栈空间 (我这里大小是0X48) ARM(stmibsp, {r1 - r12})        保护用户空间的R1-R12寄存器  ATRAP(mrcp15, 0, r7, c1, c0, 0) ATRAP(ldrr8, .LCcralign)ldmiar0, {r3 - r5}addr0, sp, #S_PC@ here for interlock avoidancemovr6, #-1@  ""  ""     ""        ""strr3, [sp]@ save the "real" r0 copied@ from the exception stack ATRAP(ldrr8, [r8, #0])@@ We are now ready to fill in the remaining blanks on the stack:@@  r4 - lr_<exception>, already fixed up for correct return/restart@  r5 - spsr_<exception>@  r6 - orig_r0 (see pt_regs definition in ptrace.h)@@ Also, separately save sp_usr and lr_usr@stmiar0, {r4 - r6} ARM(stmdbr0, {sp, lr}^) THUMB(store_user_sp_lr r0, r1, S_SP - S_PC)@ Enable the alignment trap while in kernel mode ATRAP(teqr8, r7) ATRAP( mcrnep15, 0, r8, c1, c0, 0)@@ Clear FP to mark the first stack frame@zero_fp.if\trace#ifdef CONFIG_IRQSOFF_TRACERbltrace_hardirqs_off#endifct_user_exit save = 0.endif.endm



目前由于对arm中断了解得不多。汇编部分 主要参看。。。。到时候具体处理再来看

http://blog.csdn.net/xiaojsj111/article/details/14129661

__irq_usr:usr_entrykuser_cmpxchg_checkirq_handlerget_thread_info tskmovwhy, #0bret_to_user_from_irq UNWIND(.fnend)ENDPROC(__irq_usr)

这里可以简单理解为 保存之前模式的寄存器。执行irq_handler函数。恢复寄存器,跳转到原来的指令的下一条来执行。

后续把重点放在irq_handler里面


.macroirq_handler#ifdef CONFIG_MULTI_IRQ_HANDLERldrr1, =handle_arch_irqmovr0, spadrlr, BSYM(9997f)ldrpc, [r1]#elsearch_irq_handler_default#endif9997:.endm

对于多个irq_handler。跳转到handle_arch_irq执行。对于单个handler就是arch_irq_handler_default

一下根据树莓派来确定执行流程。

我的树莓派是单个处理irq。因此跳转到arch_irq_handler_default执行

arch_irq_handler_default定义在arch\arm\include\asm\entry-macro-multi.S的line7


.macroarch_irq_handler_defaultget_irqnr_preamble r6, lr1:get_irqnr_and_base r0, r2, r6, lrmovner1, sp@@ routine called with r0 = irq number, r1 = struct pt_regs *@adrnelr, BSYM(1b)bneasm_do_IRQ#ifdef CONFIG_SMP/* * XXX * * this macro assumes that irqstat (r2) and base (r6) are * preserved from get_irqnr_and_base above */ALT_SMP(test_for_ipi r0, r2, r6, lr)ALT_UP_B(9997f)movner1, spadrnelr, BSYM(1b)bnedo_IPI#endif9997:.endm
上面的代码使用到的宏都定义在

arch\arm\mach-bcm2708\include\mach\entry-macro.S里面

.macro  get_irqnr_preamble, base, tmp
ldr \base, =IO_ADDRESS(ARMCTRL_IC_BASE)
.endm

get_irqnr_and_base得到了中断的号码等等信息。随后调用asm_do_IRQ,在编译链接的时候会连接成另外一个名字__irqentry_text_start

在arch\arm\kernel\irq.c里面

void handle_IRQ(unsigned int irq, struct pt_regs *regs){__handle_domain_irq(NULL, irq, false, regs);}/* * asm_do_IRQ is the interface to be used from assembly code. */asmlinkage void __exception_irq_entryasm_do_IRQ(unsigned int irq, struct pt_regs *regs){handle_IRQ(irq, regs);}
asm_do_IRQ->handle_IRQ->__handle_domain_irq(arch\arm\kernel\irqdesc.c)执行

详细看一下 树莓派中

get_irqnr_preamble r6, lr
1: get_irqnr_and_base r0, r2, r6, lr
movne r1, sp
@
@ routine called with r0 = irq number, r1 = struct pt_regs *
@
adrne lr, BSYM(1b)
bne asm_do_IRQ

展开以后的代码如下


.text:C05340E4                 LDR     R6, =0xF200B200         这里R6=0XF200B200的来源参看BCM2835手册。外设的物理地址是0X20000000,映射到虚拟地址后是F2000000(Page6.(page 112)7.5 Registers定义了bcm2835中断寄存器的基地址为0XB000。而IRQ BASIC PENDING的偏移是0X200,因此这里就是取得IRQbasic Pending寄存器的位置.text:C05340E8.text:C05340E8 loc_C05340E8                            ; DATA XREF: __irq_usr+A0o.text:C05340E8                 LDR     R2, [R6].text:C05340EC                 MOV     R0, #0x5F ; '_'.text:C05340F0                 AND     LR, R2, #0x300.text:C05340F4                 BICS    R2, R2, #0x300.text:C05340F8                 BNE     loc_C053412C.text:C05340FC                 TST     LR, #0x100.text:C0534100                 LDRNE   R2, [R6,#4].text:C0534104                 MOVNE   R0, #0x1F.text:C0534108                 BICNE   R2, R2, #0x680.text:C053410C                 BICNE   R2, R2, #0xC0000.text:C0534110                 BNE     loc_C053412C.text:C0534114                 TST     LR, #0x200.text:C0534118                 LDRNE   R2, [R6,#8].text:C053411C                 MOVNE   R0, #0x3F ; '?'.text:C0534120                 BICNE   R2, R2, #0x3E00000.text:C0534124                 BICNE   R2, R2, #0x40000000.text:C0534128                 BEQ     loc_C053413C.text:C053412C.text:C053412C loc_C053412C                            ; CODE XREF: __irq_usr+58j.text:C053412C                                         ; __irq_usr+70j.text:C053412C                 SUB     LR, R2, #1.text:C0534130                 EOR     R2, R2, LR.text:C0534134                 CLZ     LR, R2.text:C0534138                 SUB     R0, R0, LR.text:C053413C.text:C053413C loc_C053413C                            ; CODE XREF: __irq_usr+88j.text:C053413C                 MOVNE   R1, SP.text:C0534140                 ADRNE   LR, loc_C05340E8.text:C0534144                 BNE     __irqentry_text_start


具体的irq注册和会有第二篇

看到这里我明白了,当有外部设备发生中断的时候。比如USB插入。键盘按键。CPU接收到信号。进入中断向量中。获取中断的号码等信息。后续就是根据中断的号码执行对应的函数了




0 0