windowsCE异常和中断服务程序初探
windowsCE異常和中斷服務程序初探
??????????????????? ---------by nasiry 轉載請說明出處
1。中斷/異常相量的裝入和執行方式。??????
????? 中斷和異常都是異步發生的事件,當該事件發生,系統將停止目前正在執行的代碼轉而執行事件響應的服務程序。而事件服務程序的入口點就是中斷/異常向量所在的位置。arm的中斷向量可以是0x0開始的低地址向量,也可以是在FFFF0000位置的高向量地址。winCE下使用高地址作為trap區,所以在CE下arm使用高地址向量。下面我們來了解一下中斷/異常向量的安裝和執行過程。
?在kernelStart的過程中通過程序將如下代碼復制到ffff0000的位置.
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的位置放上如下的數據,每一項(32bit)對應一個異常的跳轉地址也就是winCE的異常/中斷向量跳轉表。該表項的內容就是發生異常后將要執行的服務程序的入口地址。具體如下。
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
????? 在上面的這些代碼/數據在內存空間上按照上述要求放置好以后,每次觸發一個異常就自動運行到相應跳轉表項所對應的地址執行。
?
2.異常/中斷服務程序
??在arm下,由于有7種異常狀態包括reset、Undef exception、software interrupt(swi)、Prefech Abort、DataAbort、IRQ、FIQ七種異常/中斷。reset僅在復位時發生,其他6種都是在系統運行時發生。當任何一個異常發生并得到響應時,ARM 內核自動完成以下動作:
?拷貝 CPSR 到 SPSR_<mode>
?設置適當的 CPSR 位:
?改變處理器狀態進入 ARM 狀態
?改變處理器模式進入相應的異常模式
?設置中斷禁止位禁止相應中斷
?更新 LR_<mode>
?設置 PC 到相應的異常向量
?同時不管異常發生在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
???????
??????? ;PendEvents? 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);
??????? ;
??????? ; mark reschedule needed
??????????????????????????? ;情況1:r0=SYSINTR_RESCHED=1
??????????????????????????? ;情況2: r0 =r0-SYSINTR_DEVICES>=SYSINTR_MAX_DEVICES???????
10????? ldrb??? r0, [lr, #bResched]???????????? ; (r0) = reschedule flag
??????? orr???? r0, r0, #1????????????????????? ; set "reschedule needed bit"
??????? strb??? r0, [lr, #bResched]???????????? ; update flag
20????? mrs???? r1, spsr??????????????????????? ; (r1) = saved status register value
??????? and???? r1, r1, #0x1F?????????????????? ; (r1) = interrupted mode
??????? cmp???? r1, #USER_MODE????????????????? ; previously in user mode?
??????? cmpne?? r1, #SYSTEM_MODE??????????????? ; if not, was it system mode?
??????? cmpeq?? r0, #1????????????????????????? ; user or system: is resched == 1
??????? ;if(SytemMode(spsr)||UserMode(spsr))&&r0!=1) return;
??????? ldmnefd sp!, {r0-r3, r12, pc}^????????? ; can't reschedule right now so return
? *************************************************************************************
??????? sub???? lr, lr, #4
??????? ldmfd?? sp!, {r0-r3, r12}
??????? stmdb?? lr, {r0-r3}
??????? ldmfd?? sp!, {r0}
??????? str???? r0, [lr]??????????????????????? ; save resume address
??????? mov???? r1, #ID_RESCHEDULE????????????? ; (r1) = exception ID
??????? b?????? CommonHandler
??????? ENTRY_END IRQHandler
??? 將spsr_irq壓入IRQ堆棧保存。為調用OEMInterruptHandler作準備。(通常中斷處理程序切換入系統態執行的目的在于避免使用終端模式下的寄存器,以方便是實現終端套嵌,這兒切入系統態時終端使能是關閉的,對于模態切換的原因我很迷惑。)OEMInterrupt需要在特權模式下執行,所以這里增加了切換入特權(SVC)模式的內容。緊接著將要用與傳遞參數的寄存器保存。設定傳入參數,r0就可以開始調用OEMInterruptHandler了,這里的調用規則遵循windowsCE的規范而不是ATPCS的規范。具體過程參考ARM Parameter Passing@msdn。下面是函數原形。int OEMInterruptHandler(unsigned int ra);這里傳入的參數就是上面的r0,事實上r0代表的參數ra并沒有實質的作用在這里僅僅是形式上的實現一下而已,不過在這兒可以看到這個傳入的ra實際上就是被中斷的地址,如果需要知道被中斷的位置可以通過ra來查詢,而msdn里面說這個參數是保留的。返回的參數也是保存在r0中。其中返回值是系統中斷類型。其中SYSINTR_RESCHED為系統時鐘中斷,每次時間片用完,該時鐘便產生中斷,并設置kData結構的bResched位,進入調度流程。如果中斷類型是系統設備中斷,那就設置PendEvents,待再次調度的時候處理中斷。所以OEMInterruptHandler必須提前就要對中斷進行響應對該中斷源設置mask,防止在這過程中同一中斷不停發生,導致中斷飽和影響程序流的執行,直道中斷處理真正完成后再次開放該中斷的mask。在這里還可以看到的是系統設備中斷號的范圍是從SYSINTR_DEVICES到SYSINTR_MAX_DEVICES,也就是從9-40一共32個設備中斷號,其中SYSINTR_FIRMWARE為8+16號,這個在編寫OAL的中斷服務程序時需要注意。如果當前的返回值既不是設備中斷號又不是調度中斷號,則讀出當前調度標示,根據該標示進行判斷是否調度/或返回.如果是進入調度流程則恢復初始的寄存器狀態,再按CommonHandler的要求保存寄存器。進入CommonHandler,等待分發。
???
2-3 FIQ服務程序
??????? 照例看看程序
??????? NESTED_ENTRY FIQHandler
??????? sub???? lr, lr, #4????????????????????? ; fix return address
??????? stmfd?? sp!, {r0-r3, r12, lr}
??????? PROLOG_END
??????? CALL??? OEMInterruptHandlerFIQ
??????? ldmfd?? sp!, {r0-r3, r12, pc}^????????? ; restore regs & return for NOP
??????? ENTRY_END FIQHandler
??????? LTORG
FIQ是arm體系下特有的異常方式,其工作過程與IRQ類似都是由外部引腳觸發但設計用途不同,IRQ用于通常的外部中斷源的處理,是作為統一、通用的與外部器件交互的手段,而IRQ僅僅用于處理周期短同時又需要快速處理的場合其觸發的事件源通常也來此外部FIQ中斷。如:更換電池、數據傳輸這類工作。可想而知FIQ講究的是快速,精干。因此FIQ服務程序通常沒有分發,而僅僅是針對單一的工作進行處理保證處理的實時性。因此FIQ的處理相對IRQ就簡單很多,直接調用
OEMInterruptHandlerFIQ進行處理后返回就完成了整個 FIQ服務程序。
2-4 DataAbort服務程序????
?? 由數據異常觸發,通常有三種指令引發數據異常,這些指令都是訪存操作,而且都是由MMU的引入后才可能會發生的情況。1.LDR/STR指令.2.SWAP指令。3.LDM/STM指令。而MMU的失效類型又分為4種:存儲訪問失效、地址對齊失效、地址變換失效、域控制器失效、訪問控制權限失效.因此當異常發生后,需要通過訪問CP15來獲知異常的產生具體原因和情況。mfc是微軟的asmarm宏匯編器專用的宏指令,相當于mcr指令。數據異常和中斷模式一樣都有可能在互鎖時發生,所以同樣需要對執行互鎖的情形進行處理。正常的情況下在保存完相關的寄存器后就會讀取CP15的c6,c5,c13三個寄存器。這三個寄存器分別是失效地址寄存器(FAR)、失效狀態寄存器(FSR)、進程號寄存器(這個翻譯得不好PCP15)然后根據具體的失效類型來進行處理。在ARM處理器中對于CP15有三種地址類型,VA,PA,MVA。VA(virtual address)也就是我們通常說的虛擬地址或邏輯地址也就是通過CP15按照PT轉換后的地址,而PA(physical Address)則是對應于AMBA上的地址,對應的是電氣介質也就是物理地址。而MVA(Modified virtual address)則是對應于Cache和TLB中轉換地址。
??????? NESTED_ENTRY??? DataAbortHandler
??????? sub???? lr, lr, #8????????????????????? ; repair continuation address
??????? stmfd?? sp!, {r0-r3, r12, lr}
??????? PROLOG_END
??????? sub???? r0, lr, #INTERLOCKED_START
??????? cmp???? r0, #INTERLOCKED_END-INTERLOCKED_START
??????? bllo??? CheckInterlockedRestart
??????? mfc15?? r0, c6????????????????????????? ; (r0) = FAR???????
??????? mfc15?? r1, c5????????????????????????? ; (r1) = FSR
??????? mfc15?? r2, c13???????????????????????? ; (r2) = process base address
???????
??????? ;? FAR=Fault address register
????;? CP = 15: CRn = 6, CRm = 0, op_1 = 0, op_2 = 0
??????? ;? FSR=Fault status register
??????? ;? CP = 15: CRn = 5, CRm = 0, op_1 = 0, op_2 = 0
??????? ;? PCP15: PID? Process ID register
????;? CP = 15: CRn = 13, CRm = 0, op_1 = 0, op_2 = 0
???????
??????? tst???? r0, #0xFE000000???????????????? ; slot 0 reference?
??????? orreq?? r0, r0, r2????????????????????? ; (r0) = process slot based address
??????? and???? r1, r1, #0x0D?????????????????? ; type of data abort
??????? cmp???? r1, #0x05?????????????????????? ; translation error?
??????? movne?? r0, #0
??????? CALLEQ? LoadPageTable?????????????????? ; (r0) = !0 if entry loaded
??????? tst???? r0, r0
??????? ldmnefd sp!, {r0-r3, r12, pc}^????????? ; restore regs & continue
??????? ;*********************************************************************
??????? ldr???? lr, =KData-4
??????? ldmfd?? sp!, {r0-r3, r12}
??????? stmdb?? lr, {r0-r3}
??????? ldmfd?? sp!, {r0}
??????? str???? r0, [lr]??????????????????????? ; save resume address
??????? mov???? r1, #ID_DATA_ABORT????????????? ; (r1) = exception ID
??????? b?????? CommonHandler
??????? ENTRY_END DataAbortHandler
在DataAbort發生后c6中的數據保存的就是導致異常的MVA地址,通過windowsCE memory layout可以了解到,當前進程的運行空間是在slot0,也就是0x0-0x1fffffff的位置,事實上這個slot上的數據僅僅是實際進程的一個副本所以如果數據異常發生在slot0就需要去找到進程所在的實際slot的存放地址,然后嘗試將內核的頁表復制到硬件實際使用的頁表以達到恢復的目的。如果復制動作成功則返回,否則進入異常分發程序CommonHandler。
2-5 PrefetchAbort服務程序
?? 對于ARM處理器來說,由于其內部使用了哈佛結構---獨立的數據的指令總線因此,在數據/指令的讀取過程中產生的異常也就很自然地可以區分開來,本質上而言,這些異常都是同屬于存儲訪問失敗產生的異常,因此這些異常都由MMU相關,在ARM手冊中DataAbort和PrefetchAbort都稱為Memory abort。Prefetch也就是在預取指令的動作后產生的,當處理器運行到這個無效的指令時(這個無效與undefined exception中的不可識別不同,是指不存在或是無法得到)就觸發該異常。所以不是所有的指令無效都產生異常,例如:一個分支程序指向一個不可訪問的區域,而之前的分支指向另一個可訪問區域時。后一個區域盡管預取無效但是由于該分支并不執行所以并不產生異常。所以prefetch的準確定義應該是prefetch and executes Abort:).在ARMV5指令集中BKPT也可以產生預取無效但由于這兒的ARM通常都是ARM9的,也就是使用ARMV4指令所以不討論BKPT的情形。由于數據異常和指令異常同屬存儲異常而且兩個異常不可能會相互中斷所以在ARM的設計上這兩個異常使用同一組寄存器abort組。
??ALTERNATE_ENTRY PrefetchAbort
??????? sub???? lr, lr, #0xF0000004??;考察產生異常的地址是否在0xf0000000-0xf0010400 ?
??????? cmp???? lr, #0x00010400????;之間,如果是進入系統調用處理
??????? bhs???? ProcessPrefAbort????? ;->>正常的預取異常 執行ProcessPrefAbort
????...??????
ProcessPrefAbort
??????? add???? lr, lr, #0xF0000000???????????? ; repair continuation address
??????? stmfd?? sp!, {r0-r3, r12, lr}
?
??????? mov???? r0, lr????????????????????????? ; (r0) = faulting address
??????? mfc15?? r2, c13???????????????????????? ; (r2) = process base address
??????? tst???? r0, #0xFE000000???????????????? ; slot 0 reference?
??????? orreq?? r0, r0, r2????????????????????? ; (r0) = process slot based address
??????? CALL??? LoadPageTable?????????????????? ; (r0) = !0 if entry loaded
??????? tst???? r0, r0
??????? ldmnefd sp!, {r0-r3, r12, pc}^????????? ; restore regs & continue
??????? ldmfd?? sp!, {r0-r3, r12}
??????? ldr???? lr, =KData-4
??????? stmdb?? lr, {r0-r3}
??????? ldmfd?? sp!, {r0}
??????? str???? r0, [lr]??????????????????????? ; save resume address
??????? mov???? r1, #ID_PREFETCH_ABORT????????? ; (r1) = exception ID
??????? b?????? CommonHandler
下面來結合windowsCE的情形。PrefetchAbort就是該服務程序的入口,在程序的一開始將lr,也就是產生異常的地址+4(流水線導致)的地址減掉0xf000 0004并比較是否在0-0x10400之間,這是為什么呢?原來windowsCE除了使用PrefetchAbort服務程序作為正常的異常處理以外還使用這個異常作為系統調用的手段。通過0xf0000000-0xf0010400這段地址的預取異常來進行系統調用。我們下面看處理預取失敗的情況,繞開系統調用的先不管。也就是ProcessPrefAbort的分支。 這個分支的內容就與上面DataAbort的內容一樣了,我就不再重復了。?
2-6異常分發?CommonHandler
到此為止我們已經了解了windowsCE對各個異常/中斷模式下的處理情況已經基本做了一個了解,但是仍然有一些情況是送到CommonHandler來處理的,下面就對這個分發程序進行分析,完整windowsCE對整個異常流程的處理。
?
??????? ALTERNATE_ENTRY CommonHandler
??????? mrs???? r2, spsr
??????? msr???? cpsr_c, #SVC_MODE:OR:0x80?????? ; switch to Supervisor mode w/IRQs disabled
??????? ldr???? r3, =KData????????????????????? ; (r3) = ptr to KData page
在CommonHandler開始系統就轉入Supervisor態來執行。?????
; Save the processor state into a thread structure. If the previous state was
; User or System and the kernel isn't busy, then save the state into the current
; thread. Otherwise, create a temporary thread structure on the kernel stack.
;
;?????? (r1) = exception ID
;?????? (r2) = SPSR
;?????? (r3) = ptr to KData page
;?????? Interrupted r0-r3, and Pc saved at (r3-0x14)
;?????? In Supervisor Mode.
??????? ALTERNATE_ENTRY SaveAndReschedule
?????? and???? r0, r2, #0x1f?????????????????? ; (r0) = previous mode
??????? cmp???? r0, #USER_MODE????????????????? ; 'Z' set if from user mode
??????? cmpne?? r0, #SYSTEM_MODE??????????????? ; 'Z' set if from System mode
??????? bne???? %F50??????????????????????????? ; reentering kernel, save state on stack
????????????????????????; 現場保護分支
????????????????????????;發生異常前模態是否是用戶態和系統態。FIQ/IRQ/SVC/Abort/Undef
??????? ldr???? r0, [r3,#pCurThd]?????????????? ; (r0) = ptr to current thread
??????? ????????????????????;?r0 =kData+pCurThd????? ????????????????????
??????? add???? r0, r0, #TcxR4????????????????? ; (r0) = ptr to r4 save
??????? ????????????????????;?r0 =kData+pCurThd+TcxR4
??????? ????????????????????;THREAD_CONTEXT_OFFSET后的0x44bytes用于備份寄存器的內容
??????? stmia?? r0, {r4-r14}^?????????????????? ; save User bank registers????????????????
??????? ****************************************************
??????? ; Save registers for fault from a non-preemptible state.
50????? sub???? sp, sp, #TcxSizeof????????????? ; allocate space for temp. thread structure
??????? cmp???? r0, #SVC_MODE
??????? bne???? %F55??????????????????????????? ; must mode switch to save state
??????? add???? r0, sp, #TcxR4????????????????? ; (r0) = ptr to r4 save area
??????? stmia?? r0, {r4-r14}??????????????????? ; save SVC state registers
??????? add???? r4, sp, #TcxSizeof????????????? ; (r4) = old SVC stack pointer
??????? str???? r4, [r0, #TcxSp-TcxR4]????????? ; update stack pointer value
??????? b?????? %B10
55
??????? msr???? cpsr, r2??????????????????????? ; switch to mode exception came from
??????? add???? r0, sp, #TcxR4????????????????? ; (r0) = ptr to r4 save area
??????? stmia?? r0, {r4-r14}??????????????????? ; save mode's register state
??????? msr???? cpsr_c, #SVC_MODE:OR:0x80?????? ; back to supervisor mode
??????? b?????? %B10??????????????????????????? ; go save remaining state
在進行統一的處理之前需要保存前態寄存器組的狀態以便后面恢復,在用戶態和系統態的情況下直接保存用戶態的寄存器。同時上面可以看到到達50的條件是前一狀態為FIQ/IRQ/SVC/Abort/Undef,也就是說為異常套嵌的情況,系統套嵌的情形前面已經處理過了。這里首先處理的是SVC下被套嵌的情形,上面可以看到SVC模式都是用于異常/中斷后的具體事件處理(eg: HandleException),所以這個流程并不是獨立存在的,因此當前寄存器就是前態寄存器,所以到這里需要重新計算stack指針的位置。而另外的FIQ/IRQ/Abort/Undef模式下的寄存器的保存則需要切換當前狀態來進行,所以在進入真正的處理程序之前需要不同的分支來保存前態寄存器狀態。可為什么前后都看不到System模式下的寄存器保存呢?這是因為系統態和用戶態使用同一組寄存器所以保存用戶態寄存器組就達到了現場保護了。這種設計完全是因為ARM分組寄存器的架構決定的,所以需要不同的處理。通過上面的處理所有的情況都已經統一的完成了現場保護的動作,下面就需要進一步處理這些異常了。
10????? ldmdb?? r3, {r3-r7}???????????????????? ; load saved r0-r3 & Pc
????????????????????????;KData之前的16byte用作傳遞參數用
????????????????????????;所以每個異常句柄最后都由將r0-r3和PC送到這個位置。???????????????
??????? stmdb?? r0!, {r2-r6}??????????????????? ; save Psr, r0-r3
??????? sub???? r0, r0, #THREAD_CONTEXT_OFFSET? ; (r0) = ptr to Thread struct
??????? str???? r7, [r0,#TcxPc]???????????????? ; save Pc
??????? mfc15?? r2, c6????????????????????????? ; (r2) = fault address
??????? mfc15?? r3, c5????????????????????????? ; (r3) = fault status
????;r0=&Kdata
????;r1=exception ID
????;r2=FAR
????;r3=FSR???
; Process an exception or reschedule request.
FirstSchedule
20????? msr???? cpsr_c, #SVC_MODE?????????????? ; enable interrupts
??????? CALL??? HandleException
??????? ldr???? r2, [r0, #TcxPsr]?????????????? ; (r2) = target status
??????? and???? r1, r2, #0x1f?????????????????? ; (r1) = target mode
??????? cmp???? r1, #USER_MODE
??????? cmpne?? r1, #SYSTEM_MODE
??????? bne???? %F30??????????????????????????? ; not going back to user or system mode
??????? ;System mode and user mode branch
??????? add???? r0, r0, #TcxR3
??????? ldmia?? r0, {r3-r14}^?????????????????? ; reload user/system mode registers
??????? ldr???? r1, =KData
??????? msr???? cpsr_c, #SVC_MODE:OR:0x80?????? ; disable all interrupts
??????? ldrb??? r1, [r1, #bResched]???????????? ; (r1) = nest level + reschedule flag
??????? cmp???? r1, #1
??????? mov???? r1, #ID_RESCHEDULE
??????? beq???? %B20??????????????????????????? ; interrupted, reschedule again
??????? msr???? spsr, r2
??????? ldr???? lr, [r0, #TcxPc-TcxR3]
??????? ldmdb?? r0, {r0-r2}
??????? movs??? pc, lr????????????????????????? ; return to user or system mode
HandleException是實際進行異常處理的函數,針對上面沒有處理完的異常進一步分析并進行處理。這個函數是沒有公開代碼的,所以沒有辦法進一步深入下去。由于處理的異常類型比較多所以這個異常處理函數的代碼量是相當大的,因此會耗費相對比較多的時鐘周期,在之前的代碼中我們都是在關閉中斷的情況下進行異常處理,如果在這里還不打開中斷的話整個異常處理過程會相當的長,這樣會很大程度上影響系統的實時性,所以在這里調用HandleException之前是將中斷重新打開的,待到處理完成再將中斷關閉。對于這些異常,如果不能處理就只有兩種情況:1.結束該進程/線程。2.掛起系統.第二種情況下掛起系統HandleException是不會返回的。因此,只有異常處理正常流程和結束線程的可能。對于返回的情況,這個時候如果返回觸發異常的地址繼續運行的話,仍然會導致異常,所以結束進程/線程都需要重新調度才能完成了。對于異常處理成功的情形,就不必調度了,直接就可以返回產生異常的地方繼續執行。在這里還要考慮套嵌(這里僅僅是指系統模式和兼管模式的異常套嵌)的情形,也就是中斷/異常已經進入調度狀態又再次產生中斷/異常,這個時候就強行取消上一次調度,進而重新調度.這用于調度過程中遇到異常恢復和剝奪的情況,如果不屬于這種情況的話就直接恢復寄存器狀態并且返回中斷點繼續執行。
; 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, {r0-r15}??????????????????? ; reload all registers & return
通過HandleException處理以后,已經完成了所有異常的處理,所以這里只是考慮反回的情況,由于這里不包含用戶模式下的處理,所以這里處理的都是特權模式,完全可以訪問kdata區域,這里就直接利用Kdata區域中的線程備份來完成恢復寄存器和返回。
總結
以上是生活随笔為你收集整理的windowsCE异常和中断服务程序初探的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: D加密最新版“威力惊人”:知名游戏破解人
- 下一篇: Windows Mobile的一些网站和