1.中斷/異常相量的裝入和執(zhí)行方式
中斷和異常都是異步發(fā)生的事件,當該事件發(fā)生,系統(tǒng)將停止目前正在執(zhí)行的代碼轉(zhuǎn)而執(zhí)行事件響應的服務程序。而事件服務程序的入口點就是中斷/異常向量所在的位置。ARM的中斷向量可以是0x0開始的低地址向量,也可以是在FFFF0000位置的高向量地址。winCE下使用高地址作為trap區(qū),所以在CE下arm使用高地址向量。
VectorINStructiONs
ldr pc, [pc, #0x3E0-8] ; reset
ldr pc, [pc, #0x3E0-8] ; undefined inSTruction
ldr pc, [pc, #0x3E0-8] ; SVC
ldr pc, [pc, #0x3E0-8] ; Prefetch abort
ldr pc, [pc, #0x3E0-8] ; data abort
ldr pc, [pc, #0x3E0-8] ; unused vector location
ldr pc, [pc, #0x3E0-8] ; IRQ
ldr pc, [pc, #0x3E0-8] ; FIQ
而在ffff03e0的位置放上如下的數(shù)據(jù),每一項(32bit)對應一個異常的跳轉(zhuǎn)地址也就是winCE的異常/中斷向量跳轉(zhuǎn)表。該表項的內(nèi)容就是發(fā)生異常后將要執(zhí)行的服務程序的入口地址。具體如下。
VectorTable
DCD -1 ; reset
DCD UndefException ; undefined instruction
DCD SWIHandler ; SVC
DCD PrefetchAbort ; Prefetch abort
IF :DEF:ARMV4T :LOR: :DEF:ARMV4I
DCD OEMDataAbortHandler ; data abort
ELSE
DCD DataAbortHandler ; data abort
ENDIF
DCD -1 ; unused vector
DCD IRQHandler ; IRQ
DCD FIQHandler ; FIQ
在上面的這些代碼/數(shù)據(jù)在內(nèi)存空間上按照上述要求放置好以后,每次觸發(fā)一個異常就自動運行到相應跳轉(zhuǎn)表項所對應的地址執(zhí)行。
2.異常/中斷服務程序
在arm下,由于有7種異常狀態(tài)包括reset、Undef exception、software interrupt(swi)、Prefech Abort、DataAbort、IRQ、FIQ七種異常/中斷。reset僅在復位時發(fā)生,其他6種都是在系統(tǒng)運行時發(fā)生。當任何一個異常發(fā)生并得到響應時,ARM 內(nèi)核自動完成以下動作:
拷貝 CPSR 到 SPSR_
設置適當?shù)?CPSR 位:
改變處理器狀態(tài)進入 ARM 狀態(tài)
改變處理器模式進入相應的異常模式
設置中斷禁止位禁止相應中斷
更新 LR_
設置 PC 到相應的異常向量
同時不管異常發(fā)生在ARM 還是Thumb 狀態(tài)下,處理器都將自動進入ARM 狀態(tài)。并且中斷使能會自動被關閉。在這個時候由于部分通用寄存器是不同模式公用的,所以還需要保存這些將會被破壞的寄存器,待到處理完成的時候恢復這些寄存器被中斷前的狀態(tài)。另外在進入異常模式后,lr的值不一定就是我們所需恢復執(zhí)行的位置,該位置受到異常類型和流水線誤差的影響。在SWI模式下,LR就是返回值。在IRQ和FIQ中LR=LR-4,DataAbort下LR=LR-8;下面分別對這些服務程序進行分析。
2-1.undef exception服務程序
undef exception在執(zhí)行到過非法的指令時產(chǎn)生,通常來模擬一些處理器不支持的功能,如浮點運算。簡單說一下undef exception的過程:當當前指令為一條處理器不支持的指令時,處理器會自動動將該指令送交各協(xié)處理器(如MMU、FPU)處理,如果這些協(xié)處理器都無法識別這條指令的時候,就產(chǎn)生該異常。下面開始看相應的代碼。
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的服務程序的入口處(已經(jīng)將不參與編譯和Thumb模式下的代碼去掉),通過lr-=4計算出觸發(fā)異常前的指令地址,同時保存r0-r3和lr入undef_exception stack用于最后恢復現(xiàn)場和取得異常指令本身,隨后進入分發(fā)程序CommonHandler.CommonHandler是一個公共的異常服務程序,它通過不同的傳入?yún)?shù)來進行處理,在這里mov r1,#ID_UNDEF_INSTR就是指定異常模式為undef Exception.
2-2.swi服務程序
按在ARM處理器的設計意圖,系統(tǒng)軟件的系統(tǒng)調(diào)用(systemCalls)都是通過SWI指令完成。SWI相當于一個中斷指令,不同的是SWI不是由外部中斷源產(chǎn)生的,同時對應于SWI的異常向量位于0xc的位置或0xffff 000c的位置。也就是說當執(zhí)行一個swi指令后,當前程序流中斷,并轉(zhuǎn)入0xc或0xffff000c執(zhí)行,同時將CPSR_mode(當前程序狀態(tài)寄存器)復制入SPSR_svc,轉(zhuǎn)入SVC模式運行(使用特權(quán)模式的寄存器組)。也就是說系統(tǒng)通過執(zhí)行SWI引發(fā)系統(tǒng)swi異常后切換入特權(quán)模式,系統(tǒng)調(diào)用功能號由swi xx后的xx決定,在運行完指定功能的代碼后返回異常時的地址并恢復用戶模式。Wince中這部分代碼是如何實現(xiàn)的。
DCD SWIHandler ; SVC《--------------------------SWI入口點。
LEAF_ENTRY SWIHandler
IF {FALSE}
…
ENDIF
movs pc, lr
ENTRY_END SWIHandler
上面IF {FALSE}到ENDIF之間的代碼在編譯的時候是得不到編譯的(事實上這部分代碼是用于開發(fā)中調(diào)試使用的,針對特殊的硬件平臺,一般與我們使用的硬件平臺無關。所以下面摘抄的代碼都不將不參與編譯的內(nèi)容寫入),因此SWI服務程序就是一句話。movs pc, lr也就是直接回到SWI的地方,同時將SPSR_svc恢復到CPSR_mode中。這個過程中并沒有進行在系統(tǒng)態(tài)執(zhí)行特定系統(tǒng)指令序的工作,而僅僅是簡單的返回,所以這不是系統(tǒng)調(diào)用,系統(tǒng)調(diào)用還需要根據(jù)調(diào)用號的不同運行指定的核心態(tài)代碼。也就是說Wince的系統(tǒng)調(diào)用不是通過SWI來完成的,而是通過其他的異常處理手段達成的。
2-3 中斷服務程序
IRQ(大概是最熟悉的異常方式了)在外部中斷源在需要向處理器請求服務時發(fā)生,比如:時鐘、外圍器件FIFO上/下溢出、按鍵等等。IRQHandler就是中斷的處理句柄,具體如下。
----------------------------------------------------------------------------------
NESTED_ENTRY IRQHandler
sub lr, lr, #4 ; fix return address
stmfd sp!, {r0-r3, r12, lr} ;保存將要用到的寄存器和lr壓入stack_irq[!--empirenews.page--]
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
上面這部分的內(nèi)容是關于互鎖的檢測,由于如信號量這些同步手段都必須作為原子操作進行,不允許打斷。所以如果中斷發(fā)生在互鎖API的執(zhí)行過程中,就需要專門的處理了。這些API都是放在INTERLOCKED_START和INTERLOCKED_END之間的,通過LR很容易就檢查出是否是INTERLOCKEDXXX的過程中。這里并不關心互鎖的實現(xiàn)就繞開這部分代碼繼續(xù)往下看,當作中斷沒有發(fā)生在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 ;->時間片已到,進行調(diào)度
beq
%B20
; interrupted, reschedule again
msr spsr, r2
ldr lr, [r0, #TcxPc-TcxR3]
ldmdb r0,
movs pc,
lr
; return to user or system mode
HandleException是實際進行異常處理的函數(shù),針對上面沒有處理完的異常進一步分析并進行處理。這個函數(shù)是沒有公開代碼的,所以沒有辦法進一步深入下去。由于處理的異常類型比較多所以這個異常處理函數(shù)的代碼量是相當大的,因此會耗費相對比較多的時鐘周期,在之前的代碼中我們都是在關閉中斷的情況下進行異常處理,如果在這里還不打開中斷的話整個異常處理過程會相當?shù)拈L,這樣會很大程度上影響系統(tǒng)的實時性,所以在這里調(diào)用HandleException之前是將中斷重新打開的,待到處理完成再將中斷關閉。對于這些異常,如果不能處理就只有兩種情況:1.結(jié)束該進程/線程。2.掛起系統(tǒng)。第二種情況下掛起系統(tǒng)HandleException是不會返回的。因此,只有異常處理正常流程和結(jié)束線程的可能。對于返回的情況,這個時候如果返回觸發(fā)異常的地址繼續(xù)運行的話,仍然會導致異常,所以結(jié)束進程/線程都需要重新調(diào)度才能完成了。對于異常處理成功的情形,就不必調(diào)度了,直接就可以返回產(chǎn)生異常的地方繼續(xù)執(zhí)行。在這里還要考慮套嵌(這里僅僅是指系統(tǒng)模式和兼管模式的異常套嵌)的情形,也就是中斷/異常已經(jīng)進入調(diào)度狀態(tài)又再次產(chǎn)生中斷/異常,這個時候就強行取消上一次調(diào)度,進而重新調(diào)度。這用于調(diào)度過程中遇到異?;謴秃蛣儕Z的情況,如果不屬于這種情況的話就直接恢復寄存器狀態(tài)并且返回中斷點繼續(xù)執(zhí)行。
; Return to a non-preemptible privileged mode.
;
; (r0) = ptr to THREAD structure
; (r2) = target mode
30 msr cpsr,
r2
; switch to target mode
add r0, r0, #TcxR0
ldmia r0,
; reload all registers & return
通過HandleException處理以后,已經(jīng)完成了所有異常的處理,所以這里只是考慮反回的情況,由于這里不包含用戶模式下的處理,所以這里處理的都是特權(quán)模式,完全可以訪問kdata區(qū)域,這里就直接利用Kdata區(qū)域中的線程備份來完成恢復寄存器和返回。