更多文章请访问 www.realview.com.cn
重入中断处理程序
如果中断处理程序再次激活了中断,然后调用一个子例程,并且产生了另个一中断,则在第二个 IRQ 产生时,存储在 lr_IRQ 中的子例程的返回地址将被破坏。 在 C 语言中使用 __irq 关键字不会对 SPSR 进行存储和恢复,而这是重入中断处理程序所要求的,因此必须使用汇编语言编写顶层中断处理程序。
在跳转到嵌套子例程或 C 函数前,重入中断处理程序必须保存 IRQ 状态、切换处理器模式并为新的处理器模式保存状态。 还必须确保堆栈对新的处理器模式是 8 字节对齐的,然后才能调用符合 AAPCS 编译的 C 代码,该代码可能使用 LDRD 或 STRD 指令或 8 字节对齐堆栈分配的数据。 有关堆栈对齐问题的详细信息,请参阅 ARM 网站上的 ABI for the ARM Architecture Advisory Note - SP must be 8-byte aligned on entry to AAPCS-conforming functions(ARM 体系结构的 ABI 告知说明 - 进入符合 AAPCS 的函数时 SP 必须是 8 字节对齐的)(ARM GENC-007024)。
在 ARMv4 及以后版本可切换至系统模式。 系统模式使用了用户模式寄存器,并启用您的异常处理程序可能需要的特权访问。 有关详细信息,请参阅 系统模式。 相反,在 ARMv4 之前的 ARM 体系结构中,必须切换到超级用户模式。
Note
该方法适用于 IRQ 和 FIQ 中断。 但是,因 FIQ 中断是最先得到服务的,而通常仅有一个中断源,所以,可能不必提供重入特性。
IRQ 处理程序中安全地再次激活中断所需的步骤如下:
构造返回地址并保存在 IRQ 堆栈中。
保存工作寄存器、非被调用方保存的寄存器以及 spsr_IRQ。
清除中断源。
切换至系统模式,保持禁用 IRQ。
检查堆栈是否是 8 字节对齐的,在必要时进行调整。
保存用户模式链接寄存器以及所做的调整,体系结构 v4 或 v5TE 的 SP_usr 使用 0 或 4。
再次激活中断并调用 C 中断处理程序函数。
C 中断处理程序返回时,禁用中断。
恢复用户模式链接寄存器和堆栈调整值。
必要时重新调整堆栈。
切换到 IRQ 模式。
恢复其他寄存器和 spsr_IRQ。
从 IRQ 返回。
Example 6.15 显示了对于 ARMv4/v5TE 处理器,这些步骤在系统模式下的实现。 Example 6.16 适用于 ARMv6 处理器。
Example 6.15.
PRESERVE8
AREA INTERRUPT, CODE, READONLY
IMPORT C_irq_handler
IRQ
SUB lr, lr, #4 ; construct the return address
STR lr, [sp, #-4]! ; and push the adjusted lr_IRQ
MRS r14, SPSR ; copy spsr_IRQ to r14
STMFD sp!, {r0-r4,r12, r14} ; save AAPCS regs and spsr_IRQ
BL identify_and_clear_source
MSR CPSR_c, #0x9f ; switch to SYS mode, IRQ is
; still disabled. USR mode
; registers are now current.
AND r1, sp, #4 ; test alignment of the stack
SUB sp, sp, r1 ; remove any misalignment (0 or 4)
STMFD sp!, {r1, lr} ; store the adjustment and lr_USR
MSR CPSR_c, #0x1f ; enable IRQ
BL C_irq_handler ; branch to C IRQ handler
MSR CPSR_c, #0x9f ; disable IRQ, remain in SYS mode
LDMFD sp!, {r1,lr} ; restore stack adjustment and lr_USR
ADD sp, sp, r1 ; add the stack adjustment (0 or 4)
MSR CPSR_c, #0x92 ; switch to IRQ mode and keep IRQ
; disabled. FIQ is still enabled.
LDMFD sp!, {r0-r4, r12, r14} ; restore registers and
MSR SPSR_csxf, r14 ; spsr_IRQ
LDMFD sp!, {pc}^ ; return from IRQ.
END
Example 6.16. 嵌套中断(ARMv6,非向量中断)
IRQ_Handler
SUB lr, lr, #4
SRSFD #0x1f! ; Save LR_irq and SPSR_irq to System mode stack
CPS #0x1f ; Switch to System mode
STMFD sp!, {r0-r3,r12} ; Store other AAPCS registers
AND r1, sp, #4
SUB sp, sp, r1
STMFD sp!, {r1, lr}
BL identify_and_clear_source
CPSIE i ; Enable IRQ
BL C_irq_handler
CPSID i ; Disable IRQ
LDMFD sp!, {r1,lr}
ADD sp, sp, r1
LDMFD sp!, {r0-r3, r12} ; Restore registers
RFEFD sp! ; Return using RFE from System mode stack
这些示例假设 FIQ 为永久启用。