Linux ARMv7架構通用中斷流程(1)【轉】


轉自:https://blog.csdn.net/zgtzqzg2020/article/details/105294193

一、ARMv7 Cortex-A系列處理器寄存器組介紹及其功能介紹


1. ARMv7 Cortex-A處理器一般共有37寄存器,其中包括:

 (1) 31個通用寄存器,包括PC(程序計數器)在內,都是32位的寄存器。

  (2) 6個狀態寄存器,都是32位的寄存器。


2. ARMv7 Cortex-A系列處理器的模式

        ARMv7 Cortex-A系列處理器共有7種處理器模式分別是:用戶模式(User)、快速中斷模式(FIQ)、普通中斷模式(IRQ)、管理模式(Supervisor SVC)、數據訪問中止模式(Abort)、未定義指令中止模式(Undefined)、系統模式(System)。在每一種處理器模式中有一組相應的寄存器。在任意一種寄存器模式下,可見的寄存器包括15個通用寄存器(R0~R14)、程序計數器(PC)、一個或者兩個狀態寄存器(CPSR、SPSR)。在所有寄存器中,有些是各個模式共用同一個物理寄存器,有些寄存器是各個模式自己擁有獨立的物理寄存器。各種模式下的寄存器組如下入所示。

       其中R0~R3主要用於子程序間傳遞參數,R4~R11主要用於保存局部變量, 但在Thumb程序中,通常智能使用R4~R7來保存局部變量;R12(Intra-Procedure-call scratch register,詳細介紹參見"Procedure Call Standard for the ARM Architecture",)用作子程序間的scratch 寄存器,即IP;R13通常用作棧指針,即SP;R14寄存器又被稱為連接寄存器,即LR,用於保存子程序以及中斷的返回地址;R15用作程序計數器(PC),由於ARM采用流水線機制,PC的值當前正在指令地址加8個字節,即PC指向當前指令的下兩條指令地址。CPSR和SPSR都是程序狀態寄存器,其中SPSR是用來保存中斷前的CPSR中的值,一邊在中斷返回后恢復處理器狀態。

3. CPSR寄存器詳解

       所有處理器模式下都可訪問當前程序狀態寄存器CPSR。CPSR中包含條件碼標志、中斷禁止位、當前處理器模式以及其他狀態和控制信息。在每種異常模式下都有一個對應的程序狀態寄存器SPSR。當異常出現時,SPSR用於保存CPSR的狀態,以便異常返回后恢復異常發生時的工作狀態。

(1)條件碼標志

        程序狀態寄存器CPSR的最高4位N、Z、C、V是條件碼標志。ARM的大多數指令可條件執行,即通過檢測這些條件碼標志來決定程序指令如何執行。

      各個標志的含義如下:

     N: 在結果有符號的二進制補碼的情況下,如果結果為負數,則N=1;如果為非負數,則N=0.

     Z:如果結果為0,則Z=1;如果結果為非零,則Z=0;

     C:其設置分一下幾種情況:

           (1)對於加法指令(包括比較指令CMN),如果產生進位,則C=1;否則C=0。

            (2)對於減法指令(包括比較指令CMP),如果產生借位,則C=0;否則C=1。

            (3)對於有移位操作的非法指令,C為移位操作中最后移出位的值。

             (4) 對於其他指令,C通常不變。

     V:對於加減法指令,在操作數和結果是有符號的整數時,如果發生溢出,則V=1;如果無溢出發生,則V=0;對於其他指令,V通常不發生變化。

 (2)控制位的作用在上圖中可以看出來。

4. CPSR與CPSR_c的區別

    CPSR有4個8位區域:標志域(F)、狀態域(S)、擴展域(X)、控制域(C).

     C控制域屏蔽字節(CPSR[7:0])

     X擴展域屏蔽字節(CPSR[15:8])

     S狀態域屏蔽字節(CPSR[23:16])

     F標志域屏蔽字節(CPSR[31:24])

     常用於MRS或MSR指令,用於CPSR的值轉移到寄存器或把寄存器的內容加載到CPSR中。如:

     MSR  CPSR_c , #0xd3


二、Linux ARMv7 Cortex-A系列處理器中斷向量表處理和代碼分析


1.  ARMv7 Cortex-A系列處理器打開關閉irq中斷

   ARMv7 Cortex-A系列處理器打開關閉irq中斷是通過改變CPSR寄存器的bit7位完成的。

   開啟和關閉當前處理器的本地中斷,會產生中斷信號,但不處理 。

   local_irq_disable()關閉中斷指令:cpsid i;

   local_irq_enable()開啟中斷指令:cpsie i;

   關閉和開啟中斷,不會產生中斷信號。

   disable_irq/enable_irq


2. linux系統為了實現異常處理引入了棧幀的概念


    
    
    
            
  1. // arch/arm/include/uapi/asm/ptrace.h
  2. /*
  3. * This struct defines the way the registers are stored on the
  4. * stack during a system call. Note that sizeof(struct pt_regs)
  5. * has to be a multiple of 8.
  6. */
  7. #ifndef __KERNEL__
  8. struct pt_regs {
  9. long uregs[ 18];
  10. };
  11. #endif /* __KERNEL__ */
  12. #define ARM_cpsr uregs[16]
  13. #define ARM_pc uregs[15]
  14. #define ARM_lr uregs[14]
  15. #define ARM_sp uregs[13]
  16. #define ARM_ip uregs[12]
  17. #define ARM_fp uregs[11]
  18. #define ARM_r10 uregs[10]
  19. #define ARM_r9 uregs[9]
  20. #define ARM_r8 uregs[8]
  21. #define ARM_r7 uregs[7]
  22. #define ARM_r6 uregs[6]
  23. #define ARM_r5 uregs[5]
  24. #define ARM_r4 uregs[4]
  25. #define ARM_r3 uregs[3]
  26. #define ARM_r2 uregs[2]
  27. #define ARM_r1 uregs[1]
  28. #define ARM_r0 uregs[0]
  29. #define ARM_ORIG_r0 uregs[17]
  30. //arch/arm/kernel/asm-offsets.c
  31. DEFINE(S_R0, offsetof( struct pt_regs, ARM_r0));
  32. DEFINE(S_R1, offsetof( struct pt_regs, ARM_r1));
  33. DEFINE(S_R2, offsetof( struct pt_regs, ARM_r2));
  34. DEFINE(S_R3, offsetof( struct pt_regs, ARM_r3));
  35. DEFINE(S_R4, offsetof( struct pt_regs, ARM_r4));
  36. DEFINE(S_R5, offsetof( struct pt_regs, ARM_r5));
  37. DEFINE(S_R6, offsetof( struct pt_regs, ARM_r6));
  38. DEFINE(S_R7, offsetof( struct pt_regs, ARM_r7));
  39. DEFINE(S_R8, offsetof( struct pt_regs, ARM_r8));
  40. DEFINE(S_R9, offsetof( struct pt_regs, ARM_r9));
  41. DEFINE(S_R10, offsetof( struct pt_regs, ARM_r10));
  42. DEFINE(S_FP, offsetof( struct pt_regs, ARM_fp));
  43. DEFINE(S_IP, offsetof( struct pt_regs, ARM_ip));
  44. DEFINE(S_SP, offsetof( struct pt_regs, ARM_sp));
  45. DEFINE(S_LR, offsetof( struct pt_regs, ARM_lr));
  46. DEFINE(S_PC, offsetof( struct pt_regs, ARM_pc));
  47. DEFINE(S_PSR, offsetof( struct pt_regs, ARM_cpsr));
  48. DEFINE(S_OLD_R0, offsetof( struct pt_regs, ARM_ORIG_r0));
  49. DEFINE(S_FRAME_SIZE, sizeof( struct pt_regs));

3. ARM異常處理類型和模式

   ARM的各種異常類型和每種異常類型處於的處理器模式,如下表所示:

4. ARM中斷處理匯編代碼分析(基於LINUX 4.4.49內核分析)

4.1 arm中斷處理總入口


    
    
    
            
  1. //arch/arm/kernel/entry-armv.S
  2. /*
  3. *注釋:
  4. * 1)ARM架構異常處理向量表起始地址__vectors_start(定義在arch/arm/kernel/vmlinux.lds).
  5. * 2)ARM架構定義7種異常包括中斷、系統調用、缺頁異常等,發生異常時處理器會跳轉到相應入口。
  6. * 3)異常向量表的起始位置由CP15協處理器的控制寄存器C1的bit13決定:
  7. * v=0,Normal exception vectors, base address 0x00000000. Software can remap this
  8. * base address using the VBAR(CP15 C12寄存器);
  9. * v=1,High exception vectors, base address 0xFFFF0000-0xFFFF001C. This base address
  10. * is never remapped.
  11. * The primary input VINITHI defines the reset value of the V bit.
  12. * VINITHI: Controls the location of the exception vectors at reset:
  13. * 0 = starts exception vectors at address 0x00000000
  14. * 1 = starts exception vectors at address 0xFFFF0000.
  15. * This pin is only sampled during reset of the processor
  16. */
  17. .section .vectors, "ax", %progbits
  18. __vectors_start:
  19. W(b) vector_rst
  20. W (b) vector_und
  21. /*
  22. *系統調用入口點:
  23. * __vectors_start + 0x1000 = __stubs_start(由arch/arm/kernel/vmlinux.lds鏈接腳本可知)
  24. * 此時PC指向系統調用異常的處理入口:vector_swi用戶態通過swi指令產生軟中斷。因為vector_swi系統
  25. * 調用異常代碼在(arch/arm/kernel/entry-common.S),其入口地址與異常向量相隔較遠,使用b指令無
  26. * 法跳轉過去。b指令只能相對當前PC跳轉 +/-32M范圍)。
  27. */
  28. W (ldr) pc, __vectors_start + 0x1000
  29. W (b) vector_pabt //取指令異常
  30. W (b) vector_dabt //數據異常--缺頁異常
  31. W (b) vector_addrexcptn
  32. W (b) vector_irq //irq中斷異常
  33. W (b) vector_fiq

4.2 以vector_irq為例進行深入分析

 vector_irq是通過vector_stub宏定義的,vector_stub宏定義尤為關鍵,ARM任何異常都是通過將r0,lr,spsr保存到異常模式的棧中(每種異常模式都有自己的棧,棧的初始化在cpu_init,見下面分析), vector_stub通過vector_\name實現其功能。


    
    
    
            
  1. //arch/arm/kernel/entry-armv.S
  2. /*
  3. *注釋:
  4. *當irq發生時,硬件自動完成如下操作:
  5. *1. arm在irq模式下有自己的lr寄存器lr_irq、spsr_irq、sp_irq.
  6. * r14_irq = lr_irq = address pf next instruction to be executed+4;
  7. *2. spsr_irq = cpsr,保存了處理器當前的狀態,中斷屏蔽位以及各種條件標志位,保存后cpsr會切換到
  8. * irq模式。
  9. *3. cpsr[4 :0] = 0b10010,設置arm為irq模式
  10. *4. cpsr[5] = 0,arm狀態執行
  11. *5. cpsr[7] = 1,禁止irq
  12. *6. pc = 0xffff0018(High exception vectors,取決於CP15協處理器的C1寄存器的配置,參看上面的分
  13. * 析), 將pc值設置成異常中斷的中斷向量地址,即vectot_irq.
  14. */
  15. /*
  16. * Interrupt dispatcher
  17. */
  18. vector_stub irq, IRQ_MODE, 4
  19. . long __irq_usr @ 0 (USR_26 / USR_32) 從用戶態下進入的irq,執行__irq_usr代碼
  20. . long __irq_invalid @ 1 (FIQ_26 / FIQ_32)
  21. . long __irq_invalid @ 2 (IRQ_26 / IRQ_32)
  22. . long __irq_svc @ 3 (SVC_26 / SVC_32) 從內核態下進入的irq,執行__irq_svc代碼
  23. . long __irq_invalid @ 4
  24. . long __irq_invalid @ 5
  25. . long __irq_invalid @ 6
  26. . long __irq_invalid @ 7
  27. . long __irq_invalid @ 8
  28. . long __irq_invalid @ 9
  29. . long __irq_invalid @ a
  30. . long __irq_invalid @ b
  31. . long __irq_invalid @ c
  32. . long __irq_invalid @ d
  33. . long __irq_invalid @ e
  34. . long __irq_invalid @ f

sp在不同的模式下有不同寄存器,在cpu_init中進行初始化。


    
    
    
            
  1. /* arch/arm/kernel/setup.c
  2. * cpu_init - initialise one CPU.
  3. *
  4. * cpu_init sets up the per-CPU stacks.
  5. */
  6. void notrace cpu_init(void)
  7. {
  8. #ifndef CONFIG_CPU_V7M
  9. unsigned int cpu = smp_processor_id();
  10. struct stack *stk = &stacks[cpu];
  11. if (cpu >= NR_CPUS) {
  12. pr_crit( "CPU%u: bad primary CPU number\n", cpu);
  13. BUG();
  14. }
  15. /*
  16. * This only works on resume and secondary cores. For booting on the
  17. * boot cpu, smp_prepare_boot_cpu is called after percpu area setup.
  18. */
  19. set_my_cpu_offset( per_cpu_offset(cpu));
  20. cpu_proc_init();
  21. /*
  22. * Define the placement constraint for the inline asm directive below.
  23. * In Thumb-2, msr with an immediate value is not allowed.
  24. */
  25. #ifdef CONFIG_THUMB2_KERNEL //此宏為定義
  26. #define PLC "r"
  27. #else
  28. #define PLC "I" //表示是立即數,其他定義可以查看GCC ARM C語言嵌入匯編語法
  29. #endif
  30. /*
  31. * setup stacks for re-entrant exception handlers
  32. * 修改幾種模式下的sp指向struct stack結構體類型變量stacks中定義的各個變量,每種模式下的棧為3個字
  33. */
  34. __asm__ (
  35. "msr cpsr_c, %1\n\t" //msr cpsr_c,(PSR_F_BIT|PSR_I_BIT|IRQ_MODE)切換為irq模式
  36. "add r14, %0, %2\n\t" //r14 = r +offset(struct stack, irq[0])
  37. "mov sp, r14\n\t"
  38. "msr cpsr_c, %3\n\t" //msr cpsr_c,(PSR_F_BIT|PSR_I_BIT|ABT_MODE)切換為abt模式
  39. "add r14, %0, %4\n\t" //r14 = r +offset(struct stack, abt[0])
  40. "mov sp, r14\n\t"
  41. "msr cpsr_c, %5\n\t" //msr cpsr_c,(PSR_F_BIT|PSR_I_BIT|UND_MODE)切換為und模式
  42. "add r14, %0, %6\n\t" //r14 = r +offset(struct stack, und[0])
  43. "mov sp, r14\n\t"
  44. "msr cpsr_c, %7\n\t" //msr cpsr_c,(PSR_F_BIT|PSR_I_BIT|FIQ_MODE)切換為fiq模式
  45. "add r14, %0, %8\n\t" //r14 = r +offset(struct stack, fiq[0])
  46. "mov sp, r14\n\t"
  47. "msr cpsr_c, %9" //msr cpsr_c,(PSR_F_BIT|PSR_I_BIT|FIQ_MODE)切換為svc模式
  48. :
  49. : "r" (stk),
  50. PLC (PSR_F_BIT | PSR_I_BIT | IRQ_MODE),
  51. "I" ( offsetof( struct stack, irq[ 0])),
  52. PLC (PSR_F_BIT | PSR_I_BIT | ABT_MODE),
  53. "I" ( offsetof( struct stack, abt[ 0])),
  54. PLC (PSR_F_BIT | PSR_I_BIT | UND_MODE),
  55. "I" ( offsetof( struct stack, und[ 0])),
  56. PLC (PSR_F_BIT | PSR_I_BIT | FIQ_MODE),
  57. "I" ( offsetof( struct stack, fiq[ 0])),
  58. PLC (PSR_F_BIT | PSR_I_BIT | SVC_MODE)
  59. : "r14");
  60. #endif
  61. }
  62. //arch/arm/kernel/setup.c
  63. struct stack {
  64. u32 irq[ 3];
  65. u32 abt[ 3];
  66. u32 und[ 3];
  67. u32 fiq[ 3];
  68. } ____cacheline_aligned;
  69. #ifndef CONFIG_CPU_V7M
  70. static struct stack stacks[NR_CPUS];
  71. #endif
  72. //arch/arm/include/uapi/asm/ptrace.h
  73. #define USR26_MODE 0x00000000
  74. #define FIQ26_MODE 0x00000001
  75. #define IRQ26_MODE 0x00000002
  76. #define SVC26_MODE 0x00000003
  77. #define USR_MODE 0x00000010
  78. #define FIQ_MODE 0x00000011
  79. #define IRQ_MODE 0x00000012
  80. #define SVC_MODE 0x00000013
  81. #define ABT_MODE 0x00000017
  82. #define UND_MODE 0x0000001b
  83. #define SYSTEM_MODE 0x0000001f
  84. #define MODE32_BIT 0x00000010
  85. #define MODE_MASK 0x0000001f
  86. #define PSR_T_BIT 0x00000020
  87. #define PSR_F_BIT 0x00000040
  88. #define PSR_I_BIT 0x00000080
  89. #define PSR_A_BIT 0x00000100
  90. #define PSR_E_BIT 0x00000200
  91. #define PSR_J_BIT 0x01000000
  92. #define PSR_Q_BIT 0x08000000
  93. #define PSR_V_BIT 0x10000000
  94. #define PSR_C_BIT 0x20000000
  95. #define PSR_Z_BIT 0x40000000
  96. #define PSR_N_BIT 0x80000000

vector_stub宏定義的分析如下:


    
    
    
            
  1. //arch/arm/kernel/entry-armv.S
  2. /*
  3. *注釋:
  4. *1. 該接口負責保存異常發生前一時刻cpu寄存器到異常模式的棧中,保存r0,lr,spsr寄存器的值到
  5. * sp_dabt或sp_irq上。
  6. *2. 此時的sp是異常狀態下的sp,這個棧只有12byte大小,在cpu_init()中初始化。
  7. *3. arm在irq/svc/abort幾種模式下sp是不能共用的。
  8. *4. 此時lr中保存的實際上是異常的返回地址,異常發生,切換到svc模式后,會將lr保存到svc模式棧中
  9. * (pt_reg->pc),最后從異常返回時再將pt_reg->pc加載如arm寄存器pc中,實現異常返回。本函數只是
  10. * 其中一個步驟,即為將異常發生時刻lr保存到svc模式棧中(pt_reg->pc)做准備。
  11. *5. spsr是異常發生那一刻(即進入異常模式前是什么模式)的cpsr狀態,如內核態下發生中斷,則spsr是
  12. * svc模式下10011,如用戶態下發生中斷,則spsr是user模式10000。
  13. *6. 此時cpu正處於異常狀態(如中斷),此時cpsr為10010。
  14. *7. 要進行真正的異常處理,需要退出異常模式進入svc模式。
  15. */
  16. /*
  17. * Vector stubs.
  18. *
  19. * This code is copied to 0xffff1000 so we can use branches in the
  20. * vectors, rather than ldr's. Note that this code must not exceed
  21. * a page size.
  22. *
  23. * Common stub entry macro:
  24. * Enter in IRQ mode, spsr = SVC/USR CPSR, lr = SVC/USR PC
  25. * * SP points to a minimal amount of processor-private memory, the address
  26. * of which is copied into r0 for the mode specific abort handler.
  27. */
  28. .macro vector_stub, name, mode, correction= 0
  29. .align 5 //強制對齊32字節對齊
  30. vector_\name:
  31. . if \correction
  32. /*
  33. *需要調整返回值,對應irq異常將lr減去4,因為異常發生時,arm將pc地址+4賦值給了lr。
  34. */
  35. sub lr, lr, #\correction
  36. .endif
  37. @
  38. @ Save r0, lr_<exception> (parent PC) and spsr_<exception>
  39. @ (parent CPSR)
  40. @
  41. /*
  42. *1. spsr中保存異常發生時刻的cpsr。
  43. *2. 此時的棧sp是異常時(irq mode或abt mode)的棧sp和svc mode里的棧sp不同。
  44. *3. save r0,lr;將r0和lr保存到異常模式的棧上[sp] = r0;[sp+4] = lr_irq;
  45. * stmia sp,{r0, lr}沒有sp!,因此sp不變。
  46. *4. r0也要入棧,r0用作傳遞參數(異常狀態下的sp)。
  47. */
  48. stmia sp, {r0, lr} @ save r0, lr
  49. mrs lr, spsr //得到異常發生時所處模式得信息
  50. /*
  51. *將spsr保存到異常模式的棧上[sp+8]=spsr_irq=lr
  52. */
  53. str lr, [sp, # 8] @ save spsr
  54. @
  55. @ Prepare for SVC32 mode. IRQs remain disabled.
  56. @
  57. /*
  58. *cpsr中保存的是異常模式:如irq 10010;dabt 10111
  59. */
  60. mrs r0, cpsr
  61. /*
  62. *1. dabt處理時,r0=r0^(0x17^0x13)=r0^0x4,bit3取反之后10011變為svc模式;
  63. *2. irq處理時:r0=10010=r0^(0x12^0x13)=r0^0x1=10011變為svc模式
  64. */
  65. eor r0, r0, #(\mode ^ SVC_MODE | PSR_ISETSTATE)
  66. msr spsr_cxsf, r0
  67. @
  68. @ the branch table must immediately follow this code
  69. @
  70. and lr, lr, # 0x0f //用戶態(user mode)lr=0;內核態(svn mode)lr=3;
  71. THUMB( adr r0, 1f )
  72. THUMB( ldr lr, [r0, lr, lsl # 2] )
  73. /*
  74. *r0=sp;
  75. *注意:
  76. *1. 此時r0中保存了異常狀態下sp棧地址,這個棧上保存了r0,lr(異常返回地址),spsr(異常發生時,cpu
  77. * 的狀態,當然異常返回時需要恢復該狀態)
  78. *2. 之后的函數會把r0中保存的異常模式的sp上信息,加載到svc模式下的sp棧上。異常處理返回時再將svc
  79. * mode 的棧加載到arm寄存器上。
  80. */
  81. mov r0, sp
  82. /*
  83. *lr中保存發生異常時arm的cpsr狀態到spsr
  84. *1. usr模式發生異常則lr=10000&0x0f;lr=pc+lr<<2 pc+0時執行 __irq_usr;
  85. *2. svc模式發生異常則lr=10011&0x0f;lr=pc+lr<<2 pc+12時執行 __irq_svc
  86. */
  87. ARM( ldr lr, [pc, lr, lsl # 2] )
  88. /* movs中s表示把spsr恢復給cpsr,上面可知spsr保存的是svc模式,不過此時中斷還是關閉的
  89. * 異常處理一定要進入svc模式原因:
  90. *(1)異常處理一定要PL1特權級。
  91. *(2)使能嵌套中斷。
  92. * 如果一個中斷模式(例如用戶態發生中斷,arm從usr進入irq模式)中重新允許中斷,
  93. * 且這個中斷模式中使用了bl指令,bl會把pc放到lr_irq中,這個地址會被當前模式下產生的中斷破壞
  94. * 這種情況下中斷無法返回。所以為了避免這種情況,中斷處理過程應該切換到svc模式,bl指令可以把
  95. * pc(即子程序返回地址)保存到lr_svc.
  96. */
  97. movs pc, lr @ branch to handler in SVC mode
  98. ENDPROC(vector_\name)

vector_irq完整代碼 (vector_stub irq, IRQ_MODE, 4)


    
    
    
            
  1. //arch/arm/kernel/entry-armv.S
  2. vector_irq:
  3. sub lr, lr, 4
  4. stmia sp, {r0, lr} @ save r0, lr
  5. mrs lr, spsr
  6. str lr, [sp, # 8] @ save spsr
  7. mrs r0, cpsr //讀取現在cpsr寄存器,
  8. eor r0, r0, # 1 //r0的第0位翻轉,在cpsr的模式M定義 usr:0b10000; svc:0b10011;
  9. msr spsr_cxsf, r0 //r0寄存器寫入spsr
  10. /*
  11. *異常發生時cpsr被保存到irq模式下的spsr中,在mrs lr, spsr中將spsr保存到lr中,
  12. *因為linux用戶態處於usr模式,內核態處於svc模式,這兩種模式在cpsr模式控制域M中只有最低兩位不同,
  13. *得到低四位,就可以判斷進入異常前處於usr模式還是svc模式,從判斷是執行下面的__irq_usr還是
  14. *__irq_svc
  15. */
  16. and lr, lr, # 0x0f
  17. mov r0, sp
  18. ldr lr, [pc, lr, lsl # 2]
  19. movs pc, lr @ branch to handler in SVC mode //發生條狀,進入svc模式
  20. . long __irq_usr @ 0 (USR_26 / USR_32) 從用戶態下進入的irq,執行__irq_usr代碼
  21. . long __irq_invalid @ 1 (FIQ_26 / FIQ_32)
  22. . long __irq_invalid @ 2 (IRQ_26 / IRQ_32)
  23. . long __irq_svc @ 3 (SVC_26 / SVC_32) 從內核態下進入的irq,執行__irq_svc代碼
  24. . long __irq_invalid @ 4 . long __irq_invalid @ 5
  25. . long __irq_invalid @ 6
  26. . long __irq_invalid @ 7
  27. . long __irq_invalid @ 8
  28. . long __irq_invalid @ 9
  29. . long __irq_invalid @ a
  30. . long __irq_invalid @ b
  31. . long __irq_invalid @ c
  32. . long __irq_invalid @ d
  33. . long __irq_invalid @ e
  34. . long __irq_invalid @ f

從用戶態進入irq中斷執行的是irq_usr代碼


    
    
    
            
  1. //arch/arm/kernel/entry-armv.S
  2. //此時arm處於svc模式執行下面代碼
  3. __irq_usr:
  4. usr_entry
  5. kuser_cmpxchg_check
  6. irq_handler //irq處理函數
  7. get_thread_info tsk
  8. mov why, # 0
  9. b ret_to_user_from_irq //中斷處理完成返回
  10. UNWIND(.fnend )
  11. ENDPROC(__irq_usr) /*
  12. * User mode handlers
  13. *
  14. * EABI note: sp_svc is always 64-bit aligned here, so should S_FRAME_SIZE
  15. */
  16. #if defined(CONFIG_AEABI) && (__LINUX_ARM_ARCH__ >= 5) && (S_FRAME_SIZE & 7)
  17. #error "sizeof(struct pt_regs) must be a multiple of 8"
  18. #endif
  19. .macro usr_entry, trace= 1, uaccess= 1
  20. UNWIND(.fnstart )
  21. UNWIND(.cantunwind ) @ don 't unwind the user space
  22. /*
  23. *arch/arm/kernel/asm-offsets.c中定義DEFINE(S_FRAME_SIZE,sizeof(struct pt_regs))
  24. *S_FRAME_SIZE=72
  25. */
  26. sub sp, sp, #S_FRAME_SIZE
  27. /*
  28. *注釋:
  29. *stmib r0!,{r1,r2} 將r1,r2的值保存到r0指向的存儲單元中(r0自動加4)
  30. *將r1-r12全部入棧,因為沒有!號所以入完棧后sp不變化,因為r1-r12在所有模式下是一組,
  31. *此時r1-r12里的值還是進入irq中斷時的值,將這些值保存到svc模式下的棧中
  32. */
  33. ARM( stmib sp, {r1 - r12} )
  34. THUMB( stmia sp, {r0 - r12} )
  35. /*
  36. *注釋:(cortex_a8_r3p2.pdf Cortex™-A8 Technical Reference Manual)
  37. *To access the Control Register, read or write CP15 with:
  38. *MRC p15, 0, <Rd>, c1, c0, 0 ; Read Control Registe
  39. */
  40. ATRAP( mrc p15, 0, r7, c1, c0, 0)
  41. /*
  42. *注釋:
  43. *.LCcralign:
  44. * .word cr_alignment
  45. */
  46. ATRAP( ldr r8, .LCcralign)
  47. /*
  48. *注釋:
  49. *ldmia r0!,{r1,r2} 將r0指向的單元中的數據讀出到r1,r2中(r0自動加4)
  50. *因r0為irq模式時sp的值,所以就是將irq模式下sp棧的內容保存到r3、r4、r5中,其中的內容irq異常發生
  51. *時r0、lr、cpsr。
  52. */
  53. ldmia r0, {r3 - r5}
  54. add r0, sp, #S_PC @ here for interlock avoidance
  55. mov r6, # -1 @ "" "" "" ""
  56. str r3, [sp] @ save the "real" r0 copied
  57. @ from the exception stack
  58. ATRAP( ldr r8, [r8, # 0])
  59. @
  60. @ We are now ready to fill in the remaining blanks on the stack:
  61. @
  62. @ r4 - lr_<exception>, already fixed up for correct return/restart
  63. @ r5 - spsr_<exception>
  64. @ r6 - orig_r0 (see pt_regs definition in ptrace.h)
  65. @
  66. @ Also, separately save sp_usr and lr_usr
  67. @
  68. stmia r0, {r4 - r6}
  69. ARM( stmdb r0, {sp, lr}^ )
  70. THUMB( store_user_sp_lr r0, r1, S_SP - S_PC )
  71. . if \uaccess
  72. uaccess_disable ip
  73. .endif
  74. @ Enable the alignment trap while in kernel mode
  75. ATRAP( teq r8, r7)
  76. ATRAP( mcrne p15, 0, r8, c1, c0, 0)
  77. @
  78. @ Clear FP to mark the first stack frame
  79. @
  80. zero_fp
  81. . if \trace
  82. #ifdef CONFIG_TRACE_IRQFLAGS
  83. bl trace_hardirqs_off
  84. #endif
  85. ct_user_exit save = 0
  86. .endif
  87. .endm
  88. .macro kuser_cmpxchg_check
  89. #if !defined(CONFIG_CPU_32v6K) && defined(CONFIG_KUSER_HELPERS)
  90. #ifndef CONFIG_MMU
  91. #warning "NPTL on non MMU needs fixing"
  92. #else
  93. @ Make sure our user space atomic helper is restarted
  94. @ if it was interrupted in a critical region. Here we
  95. @ perform a quick test inline since it should be false
  96. @ 99.9999% of the time. The rest is done out of line.
  97. cmp r4, #TASK_SIZE
  98. blhs kuser_cmpxchg64_fixup
  99. #endif
  100. #endif
  101. .endm

irq_handler 宏為中斷處理的關鍵部分,從這里會進入C語言編寫的代碼,進入linux對中斷的通用處理框架里。


    
    
    
            
  1. //arch/arm/kernel/entry-armv.S
  2. /*
  3. *注釋:
  4. *宏CONFIG_MULTI_IRQ_HANDLER在.config中有定義,會將handle_arch_irq里的值賦值給pc去執行。
  5. *給handle_arch_irq請看后面的C語言階段分析
  6. */
  7. /*
  8. * Interrupt handling.
  9. */
  10. .macro irq_handler
  11. #ifdef CONFIG_MULTI_IRQ_HANDLER
  12. ldr r1, =handle_arch_irq
  13. mov r0, sp
  14. badr lr, 9997f
  15. ldr pc, [r1] //進入C語言階段的中斷處理
  16. #else
  17. arch_irq_handler_default
  18. #endif
  19. 9997:
  20. .endm
  21. //arch/arm/kernel/entry-armv.S
  22. #ifdef CONFIG_MULTI_IRQ_HANDLER
  23. .globl handle_arch_irq
  24. handle_arch_irq:
  25. .space 4
  26. #endif

以上分析是linux對於armv架構中斷通用處理的代碼,該部分代碼跟arm架構緊密相關,此部分匯編代碼在中斷發生時執行。

下篇文章分析Linux系統在啟動階段對中斷向量表的搬移。

【作者】陳金
【出處】https://www.cnblogs.com/yifeichongtian2021/
【博客園】https://www.cnblogs.com/yifeichongtian2021/
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM