對ARM異常(Exceptions)的理解
.globl _start
_start: b
從中我們可以看出,ARM支持7種異常。問題時發生了異常后ARM是如何響應的呢?第一個復位異常很好
理解,它放在0x0的位置,一上電就執行它,而且我們的程序總是從復位異常處理程序開始執行的,因
此復位異常處理程序不需要返回。那么怎么會執行到后面幾個異常處理函數呢?
看看書后,明白了ARM對異常的響應過程,于是就能夠回答以前的這個疑問。
當一個異常出現以后,ARM會自動執行以下幾個步驟:
(1)把下一條指令的地址放到連接寄存器LR(通常是R14),這樣就能夠在處理異常返回時從正確的位置
繼續執行。
(2)將相應的CPSR(當前程序狀態寄存器)復制到SPSR(備份的程序狀態寄存器)中。從異常退出的時
候,就可以由SPSR來恢復CPSR。
(3) 根據異常類型,強制設置CPSR的運行模式位。
(4)強制PC(程序計數器)從相關異常向量地址取出下一條指令執行,從而跳轉到相應的異常處理程
序中。
至于這些異常類型各代表什么,我也沒有深究。因為平常就關心reset了,也沒有必要弄清楚。
ARM規定了異常向量的地址:
ldr pc, _undefined_instruction ;未定義的指令異常 0x4
這樣理解這段代碼就非常簡單了。碰到異常時,PC會被強制設置為對應的異常向量,從而跳轉到相應的
處理程序,然后再返回到主程序繼續執行。
這些引導程序的中斷向量,是僅供引導程序自己使用的,一旦引導程序引導Linux內核完畢后,會使用
自己的中斷向量。
嗬嗬,這又有問題了。比如,ARM發生中斷(irq)的時候,總是會跑到0x18上執行啊。那Linux內核又怎
么能使用自己的中斷向量呢?原因在于Linux內核采用頁式存儲管理。開通MMU的頁面映射以后,CPU所
發出的地址就是虛擬地址而不是物理地址。就Linux內核而言,虛擬地址0x18經過映射以后的物理地址
就是0xc000 0018。所以Linux把中斷向量放到0xc000 0018就可以了。
MMU的兩個主要作用:
(1)安全性:規定訪問權限
(2) 提供地址空間:把不連續的空間轉換成連續的。
第2點是不是實現頁式存儲的意思?
.globl _start ;系統復位位置
_start: b reset ;各個異常向量對應的跳轉代碼
ldr pc, _undefined_instruction ;未定義的指令異常
……
_undefined_instruction :
.word undefined_instruction
也許有人會有疑問,同樣是跳轉指令,為什么第一句用的是 b reset;
而后面的幾個都是用ldr?
為了理解這個問題,我們以未定義的指令異常為例。
當發生了這個異常后,CPU總是跳轉到0x4,這個地址是虛擬地址,它映射到哪個物理地址
取決于具體的映射。
ldr pc, _undefined_instruction
相對尋址,跳轉到標號_undefined_instruction,然而真正的跳轉地址其實是_undefined_instruction
的內容——undefined_instruction。那句.word的相當于:
_undefined_instruction dw undefined_instruction (詳見畢設筆記3)。
這個地址undefined_instruction到底有多遠就難說了,也許和標號_undefined_instruction在同一個
頁面,也許在很遠的地方。不過除了reset,其他的異常是MMU開始工作之后才可能發生的,因此
undefined_instruction 的地址也經過了MMU的映射。
在剛加電的時候,CPU從0x0開始執行,MMU還沒有開始工作,此時的虛擬地址和物理地址相同;另一方
面,重啟在MMU開始工作后也有可能發生,如果reset也用ldr就有問題了,因為這時候虛擬地址和物理
地址完全不同。
因此,之所以reset用b,就是因為reset在MMU建立前后都有可能發生,而其他的異常只有在MMU建立之
后才會發生。用b reset,reset子程序與reset向量在同一頁面,這樣就不會有問題(b是相對跳轉的)
。如果二者相距太遠,那么編譯器會報錯的
評論