这几天学了很多arm细节知识,个人平时实在没有深入进去
交了钱被人逼着学才会努力深究,真是贱
以前就知道arm有7种基本工作模式
FIQ 、IRQ由中断进入
UNDEF、ABORT 由程序异常进入
SVC由上电和软中断进入
user由SVC处理程序主动进入
但是还有一个system,使用和user相同的寄存器,但是又没有SPSR,同时还能执行特权指令
这么一个另类,OS把它当user用不安全,当异常和中断,又没有自动进入方式
怎么看怎么别扭,当然这也是领悟之后才意识到的,system是用来解决arm中中断可重入问题的
搜了一下,网上的关于arm可重入中断的解释只有1篇,而且没有考虑完全http://blog.chinaunix.net/u1/58640/showart_513501.html
默认的中断处理函数都会自己先主动把lr压栈先,此文没有考虑这一点,把破坏想的严重了
这样的理论知识实践中几乎没有用到,唯有追求专业的人士才会深究,这样就能满足特殊要求,做到一般人做不到的事情
首先,armcc关键字__irq不能用来编写可重入中断
当中断可重入时,在中断处理函数中使用 BL 调用子函数,这时候问题就来了,分两种情况
如果子函数不是__irq 方式申明的,没有自己压栈lr的习惯,那么此时再来一个irq中断,lr_irq就被新值冲掉
,从此陷入一个死循环。
如果子函数是会主动压栈lr的好孩子,那么仅仅在执行压栈lr的这条指令时,发生irq中断才会导致上述问题,但是触发问题的条件就变得很苛刻了
所以,这时候就需要system模式来拯救世界了
编写可重入中断必须借助以下汇编代码
IRQHandler
;LR_IRQ ,SPSR_IRQ,r12压栈,避免下一个IRQ中断将其冲掉
sub lr,lr,#4
stmfd sp!,{lr}
mrs r14,spsr
STMFD sp!,{r12,r14}
;读、清中断控制器中断源
;省略相关代码
mov r12,#IntBase
LDR r12,[r12,#IntSource]
;切换到system模式,同时使能IRQ
mrs r14,cpsr
bic r14,r14,#0x9f
orr r14,r14,#0x1f
msr CPSR_c,r14
;保存r0-r3,LR_user到user栈中,然后调用c子程序,中断源r0最为一个参数传进c处理函数
BL C_irq_handler ;看名字,就知道这个c程序是__irq形式申明的
;是一个会自己压栈LR的好孩子
LDMFD sp!,{r0-r3,lr}
;切换到IRQ模式同时禁止IRQ
mrs r12,cpsr
bic r12,r12,#0x1f
orr r12,r12,#0x92
msr CPSR_c,r12
;恢复LR_irq,SPSR_irq和工作寄存器r12,然后退出IRQ
LDMFD sp!,{r12,r14}
msr SPSR_csxf,r14
LDMFD sp!,{PC}^
可重入中断处理要点
缺省情况下ARM 中断是不可重入的,因为一旦进入异常响应状态,ARM 自动关闭中断使能。如果在异常处理过程中简单地打开中断使能而发生中断嵌套,显然新的异常处理将破坏原来的中断现场而导致出错。但有时候中断的可重入又是需要的,因此要能够通过程序设计来解决这个问题。其中有二点是这个问题的关键:
(a) 新中断使能之前必须要保护好前一个中断的现场信息,比如LR_irq 和SPSR_irq 等,这一点容易想到也容易做到。
(b) 中断处理过程中对BL 的保护
在中断处理函数中发生函数调用(BL)是很常见的,这个问题无法通过增加额外的现场保护指令来解决。一个巧妙的办法是在重新使能中断之前改变处理器的模式,也就是使上面程序中的“BL Foo”指令不要运行在IRQ 模式下。这样当新中断发生时就不会造成LR 寄存器的冲突了。考虑ARM 的所有运行模式,采用System 模式是最恰当的,因为它既是特权模式,又与中断响应无关。
下面是可重入代码(包括BL调用)的基本流程代码:
;/* @ Save return address */
sub lr, lr, #4
stmfd sp!, {lr}
;/* @@ Save spsr_fiq */
mrs r14, spsr
stmfd sp!, {r14}
;/* here should disable the corresponding interrupt */
;/* @@@ Change to SYS mode and enable system FIQ */
msr cpsr_c, #0x1F
;/* @@@@ Save SYS mode registers which next C routine will use */
stmfd sp!, {r0-r12, lr}
;/* @@@@@ Jump to C code */
IMPORT C_Fiq_Handler
bl C_Fiq_Handler
;/* @@@@ Restore SYS mode registers */
ldmfd sp!, {r0-r12, lr}
;/* @@@ Change mode back to FIQ mode and disable FIQ */
msr spsr_c, #(0x11 | Bit_FIQ)
;/* here should enable the corresponding interrupt */
;/* @@ restore spsr register */
ldmfd sp!, {r14}
msr spsr_c, r14
;/* @ return */
ldmfd sp!, {pc}^