![Rank: 8](images/default/star_level3.gif) ![Rank: 8](images/default/star_level3.gif)
- UID
- 872238
|
![](http://images.eccn.com/silabs/silicon_chip_980x60_202203.jpg)
同时不管异常发生在ARM 还是Thumb 状态下,处理器都将自动进入ARM 状态。并且中断使能会自动被关闭。在这个时候由于部分通用寄存器是不同模式公用的,所以还需要保存这些将会被破坏的寄存器,待到处理完成的时候恢复这些寄存器被中断前的状态。另外在进入异常模式后,lr的值不一定就是我们所需恢复执行的位置,该位置受到异常类型和流水线误差的影响。在SWI模式下,LR就是返回值。在IRQ和FIQ中LR=LR-4,DataAbort下LR=LR-8;具体原因我们就不讨论了,有兴趣可以参看<基于arm 的嵌入式程序开发要点>一文。下面分别对这些服务程序进行分析。
2-1.undef exception服务程序
undef exception在执行到过非法的指令时产生,通常来模拟一些处理器不支持的功能,如浮点运算。简单说一下undef exception的过程:当当前指令为一条处理器不支持的指令时,处理器会自动动将该指令送交各协处理器(如MMU、FPU)处理,如果这些协处理器都无法识别这条指令的时候,就产生该异常。下面开始看相应的代码。
NESTED_ENTRY UndefException
sub lr, lr, #4 ; (lr) = address of undefined instruction
stmdb sp, {r0-r3, lr}
mov r1, #ID_UNDEF_INSTR
b CommonHandler
ENTRY_END UndefException
上面就是undef Exception的服务程序的入口处(已经将不参与编译和Thumb模式下的代码去掉),通过lr-=4计算出触发异常前的指令地址,同时保存r0-r3和lr入undef_exception stack用于最后恢复现场和取得异常指令本身,随后进入分发程序CommonHandler.CommonHandler是一个公共的异常服务程序,它通过不同的传入参数来进行处理,在这里mov r1,#ID_UNDEF_INSTR就是指定异常模式为undef Exception.
2-2.swi服务程序
按在arm处理器的设计意图,系统软件的系统调用(SystemCalls)都是通过SWI指令完成。SWI相当于一个中断指令,不同的是SWI不是由外部中断源产生的,同时对应于SWI的异常向量位于0xc的位置或0xffff 000c的位置。也就是说当执行一个swi指令后,当前程序流中断,并转入0xc或0xffff000c执行,同时将CPSR_mode(当前程序状态寄存器)复制入SPSR_svc,转入SVC模式运行(使用特权模式的寄存器组)。也就是说系统通过执行SWI引发系统swi异常后切换入特权模式,系统调用功能号由swi xx后的xx决定,在运行完指定功能的代码后返回异常时的地址并恢复用户模式。我们看看,Wince中这部分代码是如何实现的。
DCD SWIHandler ; SVC<<--------------------------SWI入口点。
LEAF_ENTRY SWIHandler
IF {FALSE}
...
ENDIF
movs pc, lr
ENTRY_END SWIHandler
上面IF {FALSE}到ENDIF之间的代码在编译的时候是得不到编译的(事实上这部分代码是用于开发中调试使用的,针对特殊的硬件平台,一般与我们使用的硬件平台无关。所以下面摘抄的代码都不将不参与编译的内容写入),因此SWI服务程序就是一句话。movs pc, lr也就是直接回到SWI的地方,同时将SPSR_svc恢复到CPSR_mode中。这个过程中并没有进行在系统态执行特定系统指令序的工作,而仅仅是简单的返回,所以这不是系统调用,系统调用还需要根据调用号的不同运行指定的核心态代码。也就是说Wince的系统调用不是通过SWI来完成的,而是通过其他的异常处理手段达成的。
2-3 中断服务程序
IRQ(大概是最熟悉的异常方式了)在外部中断源在需要向处理器请求服务时发生,比如:时钟、外围器件FIFO上/下溢出、按键等等。IRQHandler就是中断的处理句柄,下面我们来具体看看。
----------------------------------------------------------------------------------
NESTED_ENTRY IRQHandler
sub lr, lr, #4 ; fix return address
stmfd sp!, {r0-r3, r12, lr} ;保存将要用到的寄存器和lr压入stack_irq
PROLOG_END
和上面一样,服务程序的入口处都是例行公事的计算返回位置以抵消流水线误差。再将要用到的寄存器压入STACK_IRQ,这样,准备工作就做完了。
; Test interlocked API status.
;INTERLOCKED_START EQU USER_KPAGE+0x380
;INTERLOCKED_END EQU USER_KPAGE+0x400
sub r0, lr, #INTERLOCKED_START
cmp r0, #INTERLOCKED_END-INTERLOCKED_START
bllo CheckInterlockedRestart
上面这部分的内容是关于互锁的检测,由于如信号量这些同步手段都必须作为原子操作进行,不允许打断。所以如果中断发生在互锁API的执行过程中,就需要专门的处理了。这些API都是放在INTERLOCKED_START和INTERLOCKED_END之间的,通过LR很容易就检查出是否是INTERLOCKEDXXX的过程中。这里并不关心互锁的实现就绕开这部分代码继续往下看,当作中断没有发生在interlock过程处理。
;
; CAREFUL! The stack frame is being altered here. It's ok since
; the only routine relying on this was the Interlock Check. Note that
; we re-push LR onto the stack so that the incoming argument area to
; OEMInterruptHandler will be correct.
;
mrs r1, spsr ; (r1) = saved status reg
stmfd sp!, {r1} ; save SPSR onto the IRQ stack
mov r0,lr ; parameter to OEMInterruptHandler
msr cpsr_c, #SVC_MODE:OR:0x80 ; switch to supervisor mode w/IRQs disabled
stmfd sp!, {lr} ; save LR onto the SVC stack
stmfd sp!, {r0} ; save IRQ LR (in R0) onto the SVC stack (param)
;
; Now we call the OEM's interrupt handler code. It is up to them to
; enable interrupts if they so desire. We can't do it for them since
; there's only on interrupt and they haven't yet defined their nesting.
;
CALL OEMInterruptHandler
ldmfd sp!, {r1} ; dummy pop (parameter)
ldmfd sp!, {lr} ; restore SVC LR from the SVC stack
msr cpsr_c, #IRQ_MODE:OR:0x80 ; switch back to IRQ mode w/IRQs disabled
; Restore the saved program status register from the stack.
;
ldmfd sp!, {r1} ; restore IRQ SPSR from the IRQ stack
msr spsr, r1 ; (r1) = saved status reg
ldr lr, =KData ; (lr) = ptr to KDataStruct
cmp r0, #SYSINTR_RESCHED ;->时间片已到,进行调度
beq %F10
;SYSINTR_DEVICES EQU 8 ;是否设备中断,中断号是否有效
;SYSINTR_MAX_DEVICES EQU 32
sub r0, r0, #SYSINTR_DEVICES
cmp r0, #SYSINTR_MAX_DEVICES
;由此可以看出windowsCE的系统中断号最大支持32种从9-40.
;其中第16号(24)被定义为SYSINTR_FIRMWARE
; If not a device request (and not SYSINTR_RESCHED)
ldrhsb r0, [lr, #bResched] ; (r0) = reschedule flag
bhs %F20 ; not a device request
endEvents EQU 0x340 ; offset 0x10*sizeof(DWORD) of aInfo
;device 中断
ldr r2, [lr, #PendEvents] ; (r2) = pending interrupt event mask
mov r1, #1
orr r2, r2, r1, LSL r0 ; (r2) = new pending mask
str r2, [lr, #PendEvents] ; save it
;*PendEvents = *PendEvents|(1<<InterruptNO);
; |
|