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