mips下的異常、中斷
什麼是異常
在mips中,中斷、陷阱、系統呼叫和任何可以中斷程式正常執行流的情況都稱異常
1. 外部事件 ——中斷
2. 記憶體翻譯異常
3. 其他不太常見的核心修改的程式條件
4. 程式或硬體探測到的錯誤
5. 資料完整性錯誤
6. 系統呼叫和陷入
精確異常
在執行流程中沒有任何多餘效應的異常。即當異常發生時,在受害指令之前的指令被完全執行,而受害指令及後面的指令還沒開始執行(注:說受害指令及後面的指令還沒做任何事情是不對的,實際上受害指令是處於其指令週期的第三階段剛完成,即ALU階段剛完成)。精確異常有有助於保證軟體設計上不受硬體實現的影響。
CP0中的EPC暫存器用於指向異常發生時指令跳轉前的執行位置,一般是受害指令地址。當異常時,是返回這個地址繼續執行。但如果受害指令在分支延遲槽中,則會硬體自動處理使EPC往回指一條指令,即分支指令。在重新執行分支指令時,分支延遲槽中的指令會被再執行一次。
精確異常的實現對流水線的流暢性是有一定的影響的,如果異常太多,系統執行效率就會受到影響。
異常
常規異常一般為軟體的異常,而中斷一般為硬體異常,中斷可以是晶片內部,也可以是晶片外部觸發產生。
異常發生時,跳轉前最後被執行的指令是其MEM階段剛好被執行完的那條指令。受害指令是其ALU階段剛好執行完的那條指令。
Ø 使用者特權地址的TLB重填
TLB硬體上只存在儲存一定數目的地址轉換條目,在執行一個虛擬記憶體的OS的系統中,如果如果程式得以充分的執行,應用程式就會很容易碰到一個虛擬地址在TLB中沒有的情況,一個TLB不匹配的事件就發生了。異常產生後硬體幫助異常處理程式在大約13個時鐘週期內完成TLB重填。
Ø 64位地址空間的TLB重填
為了充分利用64位CPU的地址空間,地址轉換用了一套略為不同的暫存器佈局和不同的TLB重填例程,叫做XTLB重填。
Ø 非快取的異常入口點
出於對異常處理效能的考慮,訪問中斷入口地址時都要經過快取,但是在系統啟動期間,上電或重啟時快取未經初始化不能使用。因此,在早期啟動期間訪問不需要經過快取,CP0暫存器中有個模式位SR(BEV)將異常處理入口定位於非快取的、啟動安全的kseg1記憶體區域。
Ø 奇偶/ECC校驗錯誤
Mips32cpu可以檢查到資料錯誤,這是不管SR(BEV)的狀態,快取錯誤的異常入口都在非快取區域kseg1記憶體區域。
Ø 重啟
MIPS系統把重啟看作一個不可迴歸的異常來處理。
冷啟動:CPU硬體完全被重新配置,軟體重新載入;
熱啟動:軟體完全重新初始化;
Ø 中斷
異常向量
所有的異常入口點都位於MIPS記憶體對映中不需要地址轉換的區域——非快取的kseg1段和快取的kseg0段。當SR(BEV)置位時,非快取的異常入口時固定的,但是當SR(BEV)清零時,EBase暫存器可以通過程式設計一起移動所有的異常入口到其他地址。
高優先順序異常有:冷啟動、熱重啟、非遮蔽中斷,
TLB 重填(32 位模式),
xTLB 重填(64 位模式),cache 錯誤,其他異常。
高優先順序異常入口地址有以下五個:
優先順序異常入口
異常型別 |
正常執行(BEV 為 0) |
啟動(BEV 為 1) |
冷啟動、熱重啟、非遮蔽中斷 |
0x BFC0 0000 |
0x BFC0 0000 |
TLB 重填 |
0x 8000 0000 |
0x BFC0 0200 |
xTLB 重填 |
0x 8000 0080 |
0x BFC0 0280 |
cache 錯誤 |
0x A000 0100 |
0x BFC0 0300 |
其他 |
0x 8000 0180 |
0x BFC0 0380 |
狀態暫存器(SR)
BEV=1 :非快取異常處理入口固定定位於非快取的、啟動安全的kseg1記憶體區域
BEV=0 :異常處理入口不固定,通過EBase暫存器可以程式設計移動,系統正常執行時為0
MIPS 下 TLB、Cache 都要 OS 參與管理,在其啟動時 OS 尚未接管系統,這個時候不採用 TLB、Cache 機制是很重要的。
冷啟動、熱重啟、非遮蔽中斷的入口地址始終位於 0x BFC0 0000
其他異常類入口(一般稱為通用異常入口),當 CPU 內部異常或者外部中斷髮生時, CPU 硬體設定 CAUSE 暫存器的 ExcCode( CAUSE6: 2) 位後,就跳轉到該異常入口。 ExcCode 位段用來描述通用異常型別,共 5 位,故而可以描述 2^5 = 32 個異常型別。
1. 高優先順序異常入口初始化
通用異常入口初始化,位於:[ arch/mips/kernel/traps. c]
void __init trap_init( )cache 錯誤入口初始化,位於:[ arch/mips/mm/c-r4k. c]
void __init r4k_cache_init( )
2. 通用異常處理表初始化
CAUSE 暫存器的 ExcCode 索引一張通用異常處理表 exception_handlers,它定義於:
arch/mips/kernel/traps. c]void __init trap_init( void)
異常初始化
1. EPC被置為被中斷的PC
2. 如果中斷模式是相容模式的話(普便情況),則vector offset = 0x180.
3. 如果Status[EXL] == 1,則offset = 0x180,並且EPC不變
4. 如果是TLB refill,則offset = 0x0000000,如果是其它異常,則是0x180。這裡強調一下是,mips R2後中斷有幾種模式,不同的模式它的入口還不同。
5. 如果Status[BEV] == 1,則BASE會到0xBFC00200,否則就BASE會是0x80000000,當然在MIPS32 R2後,我們設定了EBASE暫存器,這可以讓使用者選擇中斷後跳轉的指令,這與R1就不同了,它固定在0x80000000處。為了向後相容,EBASE預設情況下,它就是0x80000000處。
6. 設定cause[extcode]為異常號,中斷為0。
7. Status[EXL] = 0,這個值會產生很大的影響,它會無視Status[IE,KSU]位,而強制處於核心態和關中斷狀態。這樣做的原因是為了我們有足夠的時間去儲存現場。
8. 最近PC會跑去base+offset的地方。
異常處理
異常產生
EPC指向異常位置
1. 設定EPC指向迴歸的位置;
2. 設定SR(EXL)強迫CPU進入kernel態,並禁止所有中斷響應。
3. 設定Cause暫存器,以使軟體可以得到異常的型別資訊;還有其它一些暫存器在某些異常時 會被設定;
4. CPU開始從異常入口取指令,然後以後的所有事情都交由軟體處理了。
k0和k1暫存器用於儲存異常處理函式的地址。
異常處理函式執行完成後,會回到異常分配函式那去,在異常分配函式裡,有一個eret指令,用於迴歸原來被中斷的程式繼續執行;eret指令會原子性地把中斷響應開啟(置SR(EXL)),並把狀態級由kernel轉到user級,並返回原地址繼續執行
中斷
MIPS CPU有8個獨立的中斷位(在Cause暫存器中),其中,6個為外部中斷,2個為內部中斷(可由軟體訪問)。一般來說,片上的時鐘計數/定時器,會連線到一個硬體位上去。
Ø 全域性中斷使能位SR(IE)必須置1,否則沒有中斷響應。
Ø 設定SR(EXL)(異常級別)和SR(ERL)(錯誤級別)位(任何異常之後會立即設定這二者之一)將阻止中斷。
Ø 狀態暫存器裡有8個單獨的中斷遮蔽位SR(IM),每個對應Cause暫存器的一箇中斷位。要使能某個中斷,其對應的遮蔽位SR(IM)必須置為1。
中斷處理程式也是用通用異常入口。
中斷的響應:
handle_int ->plat_irq_dispatch->do_IRQ(irq)->generic_handle_irq -> __do_IRQ()
1. 假如Gpio中斷,系統產生中斷,通過在 CPU 的中斷引腳上,引起異常。
2. CPU 自動設定 CAUSE 的 ExcCode 位為 0, IP5 為x,並跳轉到通用異常入口0x 80000180
3. 位於通用異常入口處的簡單異常處理程式,根據 ExcCode 的值索引異常處理表(exception_handlers) ,獲取到 0 號異常的處理程式是 handle_int,並跳轉過去
4. handle_int 根據 CAUSE 之 IP 位的值跳轉到相關的中斷plat_irq_dispatch;通過簡單的計算得到中斷號,進而調用 do_IRQ 進入相應的中斷處理程式。
啟動
CPU重啟和異常幾乎相同,但重啟不會返回到被中斷的程式。利用一般異常的機制,EPC就指向重啟被偵測到時正在執行的那條指令,大多數暫存器的值會被保留。然而,重啟打斷了正常的操作、正在進行裝載的一個暫存器,或者正在進行儲存或重填的快取單元,此時重啟也許會被忽略。
利用重啟時儲存起來的狀態可以做一些有用的宕機後的除錯。
啟動順序:
1. 跳轉到主ROM程式碼。
2. 將狀態暫存器設定成為某種已知的有意義的狀態,從現在開始,可以對非快取記憶體區域進行存取操作。
3. 在初始化並且對RAM空間的完整性進行快速自檢前,啟動程式碼只能使用暫存器。
4. 對控制檯埠和診斷暫存器配置,以輸出初始化過稱中的問題
5. 分配堆疊,設定足夠多的暫存器,通用標準的C程式碼
6. 初始化快取