中断向量

1. 目标

1)中断向量概念
2)中断向量的加载
3)中断硬件准备
3) 中断处理服务vector_irq的定义
4) 中断处理服务 vector_irq的处理流程
5) 如果进入中断前是svc模式处理场景
6)如果进入中断钱是usr模式处理场景
内核版本:4.4.17

2. 中断向量表

中断向量表中保存所有中断向量的入口,__vectors_start是一个中断向量表,其中的一项:W(b) vector_irq,就是放的一个跳转指令,跳转到vector_irq。

在arch/arm/kernel/entry-armv.S中的__vectors_start的定义如下:

1209 __vectors_start:                                                                
1210         W(b)    vector_rst                                                      
1211         W(b)    vector_und                                                      
1212         W(ldr)  pc, __vectors_start + 0x1000                                    
1213         W(b)    vector_pabt                                                     
1214         W(b)    vector_dabt                                                     
1215         W(b)    vector_addrexcptn                                               
1216         W(b)    vector_irq                    //b是跳转指令                       
1217         W(b)    vector_fiq                                                      
1218                                                                                 
1219         .data    
地址异常种类
0xffff 0000复位
0xffff 0004未定义指令
0xffff 0008软中断(SWI)
0xffff 000cprefetch abort
0xffff 0010data abort
0xffff 0014address exception
0xffff 0018irq
0xffff 001cfiq

3. 中断向量加载

arch/arm/kernel/traps.c中,early_trap_init函数中:

802 void __init early_trap_init(void *vectors_base)                                 
803 {                                                                               
804 #ifndef CONFIG_CPU_V7M                                                          
805         unsigned long vectors = (unsigned long)vectors_base;                                                                                                  
806         extern char __stubs_start[], __stubs_end[];                             
807         extern char __vectors_start[], __vectors_end[];                         
808         unsigned i;                                                             
809                                                                                 
810         vectors_page = vectors_base;                                            
811                                                                                 
812         /*                                                                      
813          * Poison the vectors page with an undefined instruction.  This         
814          * instruction is chosen to be undefined for both ARM and Thumb         
815          * ISAs.  The Thumb version is an undefined instruction with a          
816          * branch back to the undefined instruction.                            
817          */                                                                     
818         for (i = 0; i < PAGE_SIZE / sizeof(u32); i++)                           
819                 ((u32 *)vectors_base)[i] = 0xe7fddef1;                          
820                                                                                 
821         /*                                                                      
822          * Copy the vectors, stubs and kuser helpers (in entry-armv.S)          
823          * into the vector page, mapped at 0xffff0000, and ensure these         
824          * are visible to the instruction stream.                               
825          */                                                                     
826         memcpy((void *)vectors, __vectors_start, __vectors_end - __vectors_start);
827         memcpy((void *)vectors + 0x1000, __stubs_start, __stubs_end - __stubs_start);
828         printk(KERN_ERR "tom __vectors_start=%x __vectors_end=%x\n",__vectors_start,__vectors_end);
printk(KERN_ERR "tom %x %x %x %x\n",*((char *)vectors+24),*((char *)vectors+25),*((char *)vectors+26),*((char *)vectors+27));
838         kuser_init(vectors_base);                                               
839                                                                                 
840         flush_icache_range(vectors, vectors + PAGE_SIZE * 2);                   
841 #else /* ifndef CONFIG_CPU_V7M */                                               
842         /*                                                                      
843          * on V7-M there is no need to copy the vector table to a dedicated     
844          * memory area. The address is configurable and so a table in the kernel
845          * image can be used.                                                   
846          */                                                                     
847 #endif                                                                          
848 }  

打印信息是:

tom __vectors_start=80630000 __vectors_end=80630020
tom 0 4 0 ea

在System.map中有__vectors_start的符号是0x80630000 ,__vectors_end是 0x80630020和打印信息一致。
1
W(b) vector_irq在内存的内容分别是: 0x00 0x04 0x00 0xea,这些是机器码,转换为整数是:0xea000400,0xea是跳转b的机器码,0x400是偏移量,怎么算出偏移量呢?
在objdump -d vmlinux中输出信息中:
123

  1. 场景是:程序中执行到0x18: b vector_irq,跳转到0x1020中的vector_irq中的偏移量怎么算?
    偏移量公式: (目标地址 - 指令地址 - 8)/ 4 = 偏移
    则: (0x1020-0x18-8)/4=0x400
    2)静态分析vmlinux中b vector_irq的机器码就是0xea00400,和内存运行时的内容一样的。

3 vector_irq定义

vector_stub的定义如下:

1027         .macro  vector_stub, name, mode, correction=0   //定义vector_stub有3个参数name,mode和correction                        
1028         .align  5   

1030 vector_\name:                                                                   
1031         .if \correction                                                         
1032         sub     lr, lr, #\correction                                            
1033         .endif                                                                  
1034                                                                                 
1035         @                                                                       
1036         @ Save r0, lr_<exception> (parent PC) and spsr_<exception>              
1037         @ (parent CPSR)                                                         
1038         @                                                                       
1039         stmia   sp, {r0, lr}            @ save r0, lr                           
1040         mrs     lr, spsr                                                        
1041         str     lr, [sp, #8]            @ save spsr                             
1042                                                                                 
1043         @                                                                       
1044         @ Prepare for SVC32 mode.  IRQs remain disabled.                        
1045         @                                                                       
1046         mrs     r0, cpsr                                                        
1047         eor     r0, r0, #(\mode ^ SVC_MODE | PSR_ISETSTATE)                     
1048         msr     spsr_cxsf, r0                                                   
1049                                                                                 
1050         @                                                                       
1051         @ the branch table must immediately follow this code                    
1052         @                                                                       
1053         and     lr, lr, #0x0f                                                   
1054  THUMB( adr     r0, 1f                  )                                       
1055  THUMB( ldr     lr, [r0, lr, lsl #2]    )                                       
1056         mov     r0, sp                                                          
1057  ARM(   ldr     lr, [pc, lr, lsl #2]    )                                       
1058         movs    pc, lr                  @ branch to handler in SVC mode         
1059 ENDPROC(vector_\name)

vector_irq的定义如下:

1080         vector_stub     irq, IRQ_MODE, 4                                        
1081                                                                                 
1082         .long   __irq_usr                       @  0  (USR_26 / USR_32)         
1083         .long   __irq_invalid                   @  1  (FIQ_26 / FIQ_32)         
1084         .long   __irq_invalid                   @  2  (IRQ_26 / IRQ_32)         
1085         .long   __irq_svc                       @  3  (SVC_26 / SVC_32)         
1086         .long   __irq_invalid                   @  4                            
1087         .long   __irq_invalid                   @  5                            
1088         .long   __irq_invalid                   @  6                            
1089         .long   __irq_invalid                   @  7                            
1090         .long   __irq_invalid                   @  8                            
1091         .long   __irq_invalid                   @  9                            
1092         .long   __irq_invalid                   @  a                            
1093         .long   __irq_invalid                   @  b                            
1094         .long   __irq_invalid                   @  c                            
1095         .long   __irq_invalid                   @  d                            
1096         .long   __irq_invalid                   @  e                            
1097         .long   __irq_invalid                   @  f                            
1098                                                       

中断硬件准备

当一切准备好之后,一旦打开处理器的全局中断就可以处理来自外设的各种中断事件了。

当外设(SOC内部或者外部都可以)检测到了中断事件,就会通过interrupt requestion line上的电平或者边沿(上升沿或者下降沿或者both)通知到该外设连接到的那个中断控制器,而中断控制器就会在多个处理器中选择一个,并把该中断通过IRQ(或者FIQ,本文不讨论FIQ的情况)分发给该processor。ARM处理器感知到了中断事件后,会进行下面一系列的动作:

  1. 修改CPSR(Current Program Status Register)寄存器中的M[4:0]。改变CPSR中M[4:0],把模式切换成IRQ MODE模式。
    2)保存发生中断那一点的CPSR值(step 1之前的状态)和PC值。(比如在用户空间,就保存用户空间的CPSR和PC;在内核空间,就保存内核空间的CPSR和PC)
    2.1 中断那一刻的CPSR,保存到IRQ MODE的SPSR中
    2.3 中断那一一刻的PC,保存到LR_IRQ中
    对于thumb state,lr_irq = PC
    对于ARM state,lr_irq = PC - 4
    3、mask IRQ exception。也就是设定CPSR.I = 1
    4、设定PC值为IRQ exception vector。
    系统现在从发生中断之前的模式(比如用户或者内核模式)切换成IRQ模式。

struct stack {
u32 irq[3];
u32 abt[3];
u32 und[3];
} ____cacheline_aligned;

static struct stack stacks[NR_CPUS];

4. vector_irq的处理流程

跳转到vector_irq模式,这个模式下 lr_irq保存发生中断时刻的PC 指针,IRQ_SPSR保存发生中断时刻的CPSR。
1)lr_irq= PC -4 (中断时刻PC寄存器) IRQ_SPSR= CPSR(中断时刻的CPSR)
2)在cpu_init中SP_IRQ指向stacks->irq地址,通过stmia sp, {r0, lr} 指令,stacks->irq[0]=r0(中断前的r0),stacks->irq[1]=LR_IRQ=PC-8(中断时刻下一条要执行的命令),通过 mrs lr, spsr和str lr, [sp, #8],stacks->irq[2]=LR_SPSR=CPSR(中断时刻的CPSR)
3)把SP_IRQ,存放到r0中。
4)PC设置为根据发生中断时刻的状态的处理函数地址,并且把IRQ模式切换为SVC模式。

1030 vector_\name:                                                                   
1031         .if  4                                                         
1032         sub     lr, lr, 4    //     /*计算返回地址(在arm流水线中,lr=pc+8,但是pc+4只译码没有执行,所以lr=lr-4) */                           
1033         .endif                                                                  
1034                                                                                 
1035         @                                                                       
1036         @ Save r0, lr_<exception> (parent PC) and spsr_<exception>              
1037         @ (parent CPSR)                                                         
1038         @                                                                       
1039         stmia   sp, {r0, lr}            @ save r0, lr           //把r0和lr寄存器的内容保存到sp寄存器保存的地址中                
1040         mrs     lr, spsr                                      //把状态寄存器spsr的内容保存到lr寄存器器中                  
1041         str     lr, [sp, #8]            @ save spsr    
          //  将lr的内容保存到SP指向的地址加上8个字节后的地址中,就是栈指针SP+8保存的内容是spsr。
1042                                                                                 
1043         @                                                                       
1044         @ Prepare for SVC32 mode.  IRQs remain disabled.                        
1045         @                                                                       
1046         mrs     r0, cpsr                            
       //将cpsr的寄存器的值保存到r0寄存器中                            
1047         eor     r0, r0, #(\mode ^ SVC_MODE | PSR_ISETSTATE)          
     //mode=IRQ_MODE=0x00000012  SVC_MODE=0x13  PSR_ISETSTATE=0x20
     // #(\mode ^ SVC_MODE | PSR_ISETSTATE) =0x12^0x13|0x20=0x1|0x20=0x21
     //eor是异或,r0和0x21异或后,保存到r0中,就是反转bit0和bit5,也就是把CPSR寄存器中的bit0和bit5反转后,保存到r0寄存器中。
     
     
1048         msr     spsr_cxsf, r0         
//把r0寄存器中的内容给SPSR,也就是把寄存器CPSR的bit0和bit5反转后,保存到SPSR寄存器。
//之前是THUMB模式转换为ARM模式,IRQ模式转换为SVC模式                                    
1049                                                                                 
1050         @                                                                       
1051         @ the branch table must immediately follow this code                    
1052         @                                                                       
1053         and     lr, lr, #0x0f           
//       把lr寄存器中的内容保留bit3-bit0。 就是把spsr的模式位保存下来      就是在进入中断模式前,CPSR就是进中断的模式存到CPSR中。 
1054  THUMB( adr     r0, 1f                  )          //THUMB是在THUMB模式下才执行的命令                             
1055  THUMB( ldr     lr, [r0, lr, lsl #2]    )                                 
1056         mov     r0, sp                                //把sp占地保存到r0中。                              
1057  ARM(   ldr     lr, [pc, lr, lsl #2]    )          /如果进入中断前是usr,则lr=0   如果是SVC,则lr=3                    
1058         movs    pc, lr                  @ branch to handler in SVC mode     //此时已经切换成SVC模式    
1059 ENDPROC(vector_\name)

问题1:
THUMB或者ARM宏在哪种模式下执行?

THUMB宏在THUMB模式下执行。
ARM宏在 ARM模式下执行。

问题2:

在这里插入图片描述

1054  THUMB( adr     r0, 1f                  )          //THUMB是在THUMB模式下才执行的命令                             
1055  THUMB( ldr     lr, [r0, lr, lsl #2]    )       

如果lr=0,则ldr lr,[r0,lr,lsl #2]后,lr=r0+lr*4=r0,因为lr是标签1的地址,但是1:的 入口地址为什么是.long __irq_usr?
因为1:和__irq_usr之间的代码不占空间。

其他

1.1 SWI异常

在__vectors_start中W(ldr) pc, __vectors_start + 0x1000 ,指的软中断SWI。
如下图中所示,在System.map中查看,可以看到 __vectors_start + 0x1000,也就是 __stubs_start。
stubs_start
查看__stubs_start的定义如下,可以看到__stubs_start指向vector_swi标签。

1067 __stubs_start:                                                                  
1068         @ This must be the first word                                           
1069         .word   vector_swi 

1.2 CPSR寄存器

M[4:0]模式
0b10000用户
0b10001FIQ
0b10010IRQ
0b10011管理模式
0b10111中止
0b11011未定义
0b11011系统
bit5工作状态
1Thumb
0ARM

注:
所有处理器模式下都可访问当前程序状态寄存器CPSR。CPSR中包含条件码标志、中断禁止位、当前处理器模式以及其他状态和控制信息。在每种异常模式下都有一个对用的程序状态寄存器SPSR。当异常出现时,SPSR用于保存CPSR的状态,以便异常返回后恢复异常发生时的工作状态。

c - control field mask byte(xPSR[7:0])
x - extension field mask byte(xPSR[15:8])
s - status field mask byte(xPSR[23:16)
f - flags field mask byte(xPSR[31:24]).
老式声明方式:cpsr_flg,cpsr_all在ADS中已经不在支持
cpsr_flg对应cpsr_f
cpsr_all对应cpsr_cxsf

参考

分析在linux中的中断是如何运行的,以及中断3大结构体:irq_desc、irq_chip、irqaction
ARM中断表与响应流程
Linux系统调用过程中user栈的保存与恢复
linux中断中(异常向量详解)

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值