# Arm Programmer's Guide XI - Exception Handling 學習筆記
<h1>1. 前言</h1>
此筆記為學習 [ARM® Cortex™-A Series Programmer's Guide
Version: 4.0 中第十一章 Exception Handling ](https://developer.arm.com/documentation/den0013/d/Exception-Handling?lang=en) 的心得筆記。主旨在解釋 Armv7 Aarch32 下關於 Exception 的行為跟設計。
<h1>2. 概述</h1>
例外或者異常 (Exception) 有時也被稱為陷阱 -- Trap (但 Trap 更多時候表示的是被軟體中斷引發的 Exception,用來切換核心模式用)。異常是一種特殊的情況,當核心進到這狀況時 (通常是因為遇到某種錯誤、或要改變核心模式),他會中斷現在正在執行的流程,轉而去特權 (Privilage) 模式中一段預留給 Exception 的位置 (Exception Vector) 執行一個特別的軟體流程 (Exception Handler),通常這流程會去試著解決遇到的錯誤,這行為稱為處理異常 (Handling an Exception)。當一個 Exception 被處理完成後,系統會回到一開始發生中斷的流程中 (如果有辦法的話),並繼續向下執行。
<h1>3. Types of Exception</h1>
核心總共有以下九種模式:
1. User (唯一的 Unprivileged 模式)
2. FIQ, Fast Interrupt Request
3. IRQ, Interrupt Request
4. SVC, Supervisor
5. ABT, Abort
6. UND, Undefined
7. SYS, System
8. HYP, Hypervisor
9. MON, Monitor
而異常的種類和核心模式息息相關,因為基本上異常也可想成是進入其中一個特權模式的方法:
<h2>3.1 Interrupts</h2>
中斷 (Interrupt) 有兩種 -- FIQ 和 IRQ。當中斷發生,表示核心的 Input Pin 收到了來自外部設備的實際訊號,核心模式會切成 FIQ 或者 IRQ。也就是說,當一個外部設備透過實際電路在核心觸發中斷時,如果核心的中斷功能沒有被關掉,那麼++核心會先執行完正在執行的指令++,然後發起相對應的中斷異常。
[CPSR (Current Program Status Register)](https://developer.arm.com/documentation/den0013/d/ARM-Processor-Modes-and-Registers/Registers/Program-Status-Registers?lang=en) 的 BIT F 和 I 分別用來控制是否要關閉 FIQ 和 IRQ 產生的中斷異常。如果該 BIT 是 1,則關閉,核心會擱置相對應的硬體訊號,不會因此產生異常 (但該訊號不會因此而消失,若中斷異常再度開啟,異常就會產生)。
FIQ 除了比 IRQ 有更高的優先權之外,FIQ 還有以下兩點的優勢:
1. FIQ 在 Exception Vector 的位置位於最後面 (可見本篇[第五章](https://hackmd.io/@uMqav-XESBCsrtZEx_Jpug/Arm_Programmer_Guide_Exception_Handling_Study_Note#5-Vector-Table)),這使他可以直接在該位置實作其 Handler 函式,而省下執行跳轉動作所需的核心週期。
2. FIQ 擁有更多的獨立暫存器,這使其 Handler 函式可以在開頭 / 結尾花更少時間去保存 / 恢復中斷前的暫存器。
幾乎所有的系統會用中斷控制器 (GIC, Genric Interrupt Controller) 來彙整所有的中斷信號源,它能夠對中斷源做進一步的分類、塞選還有優先級排序 (像是可以忽略特定的中斷源),更詳細內容請見 [Arm Programmer's Guide XII - Interrupt Handling 學習筆記](https://hackmd.io/@uMqav-XESBCsrtZEx_Jpug/Arm_Programmer_Guide_Interrupt_Handling_Study_note)。
由於 IRQ 和 FIQ 中斷異常並不是因為核心執行了特定指令而產生,而是由外部設備在和軟體執行流程沒關的時間點觸發,IRQ 和 FIQ 異常被歸類為非同步異常 (Asynchronous Exception)。這個概念在後面會有更詳細的說明。
<h2>3.2 Aborts</h2>
當 Abort 異常發生,核心模式會切成 Abort。其來源有很多種:
1. **Prefetch Abort**
核心為了提升效率,會將未來要執行的指令預先從記憶體空間載入進核心的指令流 (Pipeline) 中(這步驟較花時間),然後再從該 Pipeline 中依序取出 (這步驟能快速完成) 指令來執行。這過程中的各環節出錯都會發起 Prefetch Abort,根據發生位置可以分成核心內或核心外記憶體系統所觸發:
* 【核心外的記憶體系統觸發的 Prefetch Abort】
在核心外 Prefetch 指令時 (例如正在主記憶體中查找指令 -- Translation Table Walk), 若遇到像是硬體錯誤、無法修復的 ECC 錯誤等問題,記憶體可以透過實際訊號於核心中發起 Prefetch Abort。
* 【核心內觸發的 Prefetch Abort】
由核心執行到錯誤的 Prefetch 指令而觸發,Exception 會發生在執行該指令之「前」。
錯誤指令在被預先載入 (Prefetch) 核心執行 Pipeline 的時候並不會發生 Prefetch Abort,需一直到核心真的要執行該錯誤指令時才會發生。所以如果一個有問題的指令已經被載入 Pipline ,但是在核心執行到他之前 Pipeline 就被清洗掉 (Flush),那麼 Prefetch Abort 並不會發生。一條指令被認為是有問題的有可能是因為找不到他的實體記憶體位置,或者沒有該記憶體位置的存取權限等等。
2. **Data Abort**:
Data Abort 一樣可以發生於核心中的 MMU 或者核心外的記憶體設備:
* 【核心外的記憶體系統觸發的 Data Abort】
像是記憶體在查找映射表 (Translation Table Walk)、或在將 Write-Buffer 中的值實際寫入記憶體空間時,若遇到硬體上或其他的錯誤無法完成,記憶體可以透過實際訊號於核心中發起 Data Abort。
* 【核心內發起的 Data Abort】
Exception 會發生在 MMU 試圖存取非法記憶體位置之「後」。可能是因為找不到該位置的實體記憶體或者是沒有該位置的存取權限所致。
3. MMU 也可以觸發 Abort 異常來幫應用程式進行記憶體空間的動態分配 (Dynamically allocate)。
<h3>3.2.1 Synchronous / Asynchronous Abort</h3>
1. **Synchronous Abort**: 當一個 Abort 是因為核心執行或試圖執行某個指令而觸發時,該 Abort 為同步 (Synchronous) Abort。這種 Abort 的發生位置是很明確的,系統能從 Exception 的 Return Address 得知 (詳情請參考[第六章](https://hackmd.io/@uMqav-XESBCsrtZEx_Jpug/Arm_Programmer_Guide_Exception_Handling_Study_Note#6-Exception-Handling-Flow)的表格)。
核心內的 Prefetch Abort 和任何 MMU 觸發的 Abort 一定是 Synchronous Abort。
2. **Asynchronous Abort**: 相反地,如果一個 Abort 不是因為執行某個指令而觸發,則該 Abort 為非同步 (Asynchronous) Abort。核心不一定會知道非同步 Abort 是哪條指令觸發的。如果能知道,則稱為 Precise Asynchronous Abort;若不知道則為 Imprecise Asynchronous Abort (大部分)。請見以下範例:
* 系統可以將因在外部記憶體中查找映射表 (Translation Table Walk) 遇到問題產生的 Abort 設計成 Precise 的,但這需要特殊的設計,不是一定。
* Write-buffer 會讓實際的寫發生在寫指令之後,使其產生的 Abort 為 Imprecise。就算是針對映射給某個 Memory-mapped 周邊的 Strongly-ordered 區段,在寫指令完成之後,若該周邊因此發出 Abort,也可能是在許多 Cycle 後,但核心早已繼續執行接續的指令了。
* 上一個例子的邏輯也可以用來說明為什麼在實作設備偵測 (Device Probing) 時需要特別留意,因為透過對該設備記憶體區段做讀寫來判斷其存在與否時,就算將該區段標為 Strongly-ordered,若該設備不存在或沒有回應,硬體總線邏輯也是在非同步且 Imprecise 的情況下向核心發出中斷。
由於 Imprecise Asynchronous Abort 無法得知出錯時間點,所以也沒有辦法在修正錯誤後回到錯誤點。此類 Exception Handler 的重點應該放在該如何將出錯的程式安全地移除。
CPSR 的 BIT A 可以用來暫時阻擋 Asynchronous Abort。如果該 BIT 為 1,則任何非同步 Abort 會被擱置,也就是說核心會收到這個 Abort 但他不會發起 Exception,一直到該 BIT 被清為 0 為止。在多工環境下切換工作前,需要注意使用記憶體屏障指令 (Barrier instruction) 將這個 BIT 清 0 並處理當下應用的非同步 Abort,否則若工作已經切換才發起前一個工作產生的非同步 Abort,該非同步 Exception Handler 有可能會殺掉錯誤的應用程式。
<h2>3.3 Reset</h2>
所有的核心都有一個 input pin 用來接收 Reset 訊號,只要一收到, Reset Exception 就會「馬上」被發起。他是擁有最高權限的 Exception,而且沒有辦法被關掉。
<h2>3.4 Exception generating instructions</h2>
特定的指令也可以引起 Exception,通常是用來切換不同特權等級 (PL, Privilage Level) 用的: (更多 PL 描述可以參考: [ARM Processor Modes and Registers](https://developer.arm.com/documentation/den0013/d/ARM-Processor-Modes-and-Registers?lang=en))
1. SVC (Supervisor Call): 讓核心從 User mode (NPL0, Non-Secure world Privilage Level 0) 切換到 Sys mode (NPL1)。
2. HVC (Hypervisor Call): 只有在虛擬化 (Virtualization Extension) 有被實作時有用,能讓被虛擬的作業系統 (Guest OS, 位於 NPL1) 進到 HYP 模式 (NPL2)。
3. SMC (Secure Monitor Call): 只有在安全模式 (Security Extension) 有被實作時有用,能讓正常區域 (Normal World) 進到安全區域 (Secure World) 的 Monitor mode (SPL1, Secure World Privilage Leve 1)。
4. 任何核心認不得的指令都會觸發 UNDEFINED Exception。
上述一至三的指令都會切換核心的特權階級,除了 N/SPL0 之外的階級都有其各自的 Exception Vector,他們分別存在各自專屬的暫存器中:
1. N/SPL1 用各自的 [VBAR (Vector Base Address Register)](https://developer.arm.com/documentation/ddi0601/2024-12/AArch32-Registers/VBAR--Vector-Base-Address-Register?lang=en) 來記錄 Exception Vector 起始位置 (但如果 [SCTLR](https://developer.arm.com/documentation/ddi0601/2024-12/AArch32-Registers/SCTLR--System-Control-Register?lang=en) 中 BIT V 是 1 的話 ,Exception Vector 起始位置則固定為 0xFFFF0000)。
2. NPL2 (HYP) 用 [HVBAR (Hyp Vector Base Address Register)](https://developer.arm.com/documentation/ddi0601/2024-12/AArch32-Registers/HVBAR--Hyp-Vector-Base-Address-Register?lang=en) 紀錄其 Exception Vector 起始位置。
3. SPL3 (MON) 用 [MVBAR (Monitor Vector Base Address Register)](https://developer.arm.com/documentation/ddi0601/2024-12/AArch32-Registers/MVBAR--Monitor-Vector-Base-Address-Register?lang=en) 紀錄其 Exception Vector 起始位置。
<h1>4. Exception Priority</h1>
若有多個 Exception 在同一個指令週期內發生,核心會根據各 Exception 的優先級順序來決定要先處理哪一個異常,他們的優先級順序如下:
1. Reset (最高、最優先處理)
2. Data Abort
3. FIQ
4. IRQ
5. Prefetch Abort
6. Undefinied Instruction
7. SVC
8. HVC (最低、最後處理)
但異常同時發生的情況並不少見,畢竟一個指令週期是非常短的時間,更需要關注的應該是他們之間的搶佔優先權,也就是誰可以搶佔誰,但事實上**當異常前後發生時,基本上後者都可以搶佔前者,並不存在搶占優先權的概念**。不過 Arm 還是有提供屏蔽非同步中斷 (FIQ、IRQ 等等) 異常的功能 (被屏蔽的的中斷不會消失,等到屏蔽結束後,異常還是會被發起),來防止當前的異常被特定的非同步中斷搶占,有些甚至會在進到異常後自動屏蔽掉,使行為上看起來有搶佔上的優先權之分。下方列出如何於 [CPSR]((https://developer.arm.com/documentation/den0013/d/ARM-Processor-Modes-and-Registers/Registers/Program-Status-Registers?lang=en)) 中屏蔽特定的非同步異常:
1. CPSR BIT F, Pending FIQ。
2. CPSR BIT I, Pending IRQ。
3. CPSR BIT A, Pending Asynchronous Abort。
下表則為透過 Exception 進到各核心模式時,Arm 會自動屏蔽掉的非同步中斷表:
| Exception | Core Mode | CPSR Interrupt Mask |
| --------------------- | ---------- | ------------------- |
| Reset | Supervisor | F = 1, I = 1 |
| Undefined instruction | Undef | I = 1 |
| SVC (Supervisor Call) | Supervisor | I = 1 |
| Prefetch Abort | Abort | I = 1 |
| Data Abort | Abort | I = 1 |
| HVC (Hypervisor Call) | HYP | I = 1 |
| IRQ Interrupt | IRQ | I = 1 |
| FIQ Interrupt | FIQ | F = 1, I = 1 |
若將 Reset 這個強制 Reset 系統的 Exception 排除來看,由上表可知,除了 FIQ 自己外,任何的 Exception 都會將 IRQ 屏蔽掉,但是不會屏蔽掉 FIQ。舉例來說,當 Data Abort 和 FIQ 同時發生,那麼因為 Data Abort 的優先級較高,核心會先處理 Data Abort,但因為 Data Abort 沒有屏蔽掉 FIQ,所以 FIQ 會搶佔 Data Abort,讓核心轉往處理 FIQ。當 FIQ 處理完後會回到 Data Abort 中,Data Abort 處理完後才會回到原本的正常流程中;相反的,假設是 Data Abort 和 IRQ 同時發生,Data Abort 一樣會因為較高的優先權而先被處理,但因為開始處理 Data Abort 會伴隨著屏蔽 IRQ,所以 IRQ 中斷會被擱置,直到 Data Abort 處理完並且關掉 IRQ 的屏蔽。
上表中的屏蔽雖然是自動執行的,但不代表不可改變,FIQ Exception Handler 一樣可以在其中將 FIQ 和 IRQ 屏蔽關掉、FIQ 外的 Exception Handler 也可以在其中去打開 IRQ 的屏蔽。
<h1>5. Vector Table</h1>
前面 3.4 章有先提到過各特權階級下的異常向量表 (Exception Vector) 起始位置,如果 [SCTLR](https://developer.arm.com/documentation/ddi0601/2024-12/AArch32-Registers/SCTLR--System-Control-Register?lang=en) 中 BIT V 是 0 的話,會用各自專屬的暫存器來儲存 (VBAR, HVBAR, MVBAR);如果 BIT V 是 1,則不再使用 VBAR ,改將 Exception Vector 固定放在 0xFFFF0000 (稱為 HIVECS) 的位置。
以下是 Exception Vector 的各 Handler 分布:
| Offset | Non-secure | Secure | Hypervisor | Monitor |
| ------ | -------------- | -------------- | ----------------------- | -------------- |
| 0x00 | Unused | Reset | Reset | Unused |
| 0x04 | UNDEF | UNDEF | UNDEF from HYP | Unused |
| 0x08 | SVC | SVC | SMC | SMC |
| 0x0c | Prefetch Abort | Prefetch Abort | Prefetch Abort from HYP | Prefetch Abort |
| 0x10 | Data Abort | Data Abort | Data Abort from HYP | Data Abort |
| 0x14 | Unused | Unused | HYP Mode Entry | Unused |
| 0x18 | IRQ Interrupt | IRQ Interrupt | IRQ Interrupt | IRQ Interrupt |
| 0x1c | FIQ Interrupt | FIQ Interrupt | FIQ Interrupt | FIQ Interrupt |
:::info
關於上表有以下的補充說明:
1. Hypervisor 和 Monitor 模式異常向量 (HVBAR 和 MVBAR) 的 0x08 位置都有實作 SMC 的 Exception Handler。一般來說 SMC 指令都會跳去 Monitor 模式執行 SMC Exception Handler。但如果 [HCR (Hypervisor Configuration Register)](https://developer.arm.com/documentation/ddi0601/2024-12/AArch32-Registers/HCR--Hyp-Configuration-Register?lang=en) 的 BIT TSC 為 1,那麼 PL1 中的 SMC 指令會去 HYP mode 執行 SMC Exception Handler 而不是去 SMC 中。
2. HYP 的欄位有特別強調 from HYP,是因為 HYP 位於 NPL2,而 NPL2 只有 HYP 一個核心模式,所以在 HYP 中發生的任何異常都不會改變其核心模式,都還是會在維持 HYP 的情況下去執行 HYP 異常向量表中的 Exception Handler。也就是說 HYP 的異常向量表是專門用來處理 HYP 中的異常用的,而其他的異常向量表則處理可能來自不同的核心模式的異常。舉例來說,Non-secure 的異常向量表會處理來自 Non-secure 的 user 和 sys 模式的異常;Monitor 模式的異常向量表也可能處理來自其他模式下發生的異常,像是 [SCR (Secure Configuration Register)](https://developer.arm.com/documentation/ddi0601/2024-12/AArch32-Registers/SCR--Secure-Configuration-Register?lang=en) 中的 BIT EA 能決定 External Abort 是否要交給 Monitor mode 處理。
3. HYP 異常向量表的 0x14 偏移量為 HYP 的入口點,而進入 HYP 的方法除了 HVC 其實還有很多種可能。這時就需要另一個暫存器 [HSR (Hyp Syndrome Register)](https://developer.arm.com/documentation/ddi0601/2024-12/AArch32-Registers/HSR--Hyp-Syndrome-Register?lang=en) 來說明進入的原因為何。舉例來說,若 HSR 的 BIT EC (Exception Class) 為 b100100 則表示是因為較低特權階級的 Data Abort 而進入 HYP、 BIT ISS (Instruction Specific Syndrome) 則可以提供更細節的資訊 (像 BIT WnR 能指出是讀還是寫造成 Data Abort)。
:::
上表可以看出每一個欄位都只有 32 Bit (1 Word) 的空間,只擺得下單一一條指令 (或者 Thumb mode 下可以擺兩條),所以基本上都只會擺下面兩種跳轉指令的其中一種:
1. **B \<Label>**
B 指令可以跳轉至相對於當前 PC 的指定位置,他被翻譯成機器語言後的格式如下:

其中 Offset 使用了 24 BIT 來記錄,又由於單位是 Word,所以總共可以跳至 PC 前後 32MB 的位置。如果 Exception Handler 的實體在這個範圍內的話,那就可以使用 B \<Label> 的方式來跳去執行。
2. **LDR PC, [PC, #offset]**
這個方法就不像 B 有跳轉範圍限制,但會花費較多的週期來完成。
<h1>6. Exception Handling Flow</h1>
以下為當異常發生時,Arm 核心會**自動進行**的流程:
1. 將 CPSR 複製到 [SPSR (Saved Program Status Register)](https://developer.arm.com/documentation/ddi0601/2024-12/AArch32-Registers/SPSR--Saved-Program-Status-Register?lang=en)。
2. 將問題發生位置存於 Exception 相對應核心模式的 LR (Link Register) 中。
但由於 Exception 發生時有時已經執行了引發異常的指令,有的還沒等等原因,若真的要回到出問題時的指令,不能直接跳回 LR 中的值,而需要做以下的調整:
| Exception | Adjustment | Return instruction | Instruction return to |
| -------------- | ---------- | ------------------ | --------------------------- |
| SVC | 0 | MOVS PC, LR | Next instruction |
| Undef | 0 | MOVS PC, LR | Next instruction |
| Prefetch Abort | 0 | SUBS PC, LR, #4 | Abort instruction |
| Data Abort | 0 | SUBS PC, LR, #8 | Next instruction if precise |
| FIQ | 0 | SUBS PC, LR, #4 | Next instruction |
| IRQ | 0 | SUBS PC, LR, #4 | Next instruction |
3. 修改 CPSR 來切換核心模式並確保Exception 前後的 State 和端序一致:
* 修改 Mode Bits (BIT[4:0]) 將核心模式切為 Exception 相對應的模式。
* 根據 SCTLR 的 BIT TE (控制 Exception 是跑在 Arm32 State 還是 Thumb32 State) 來修改 CPSR 的 BIT T (控制核心是否跑在 Thumb State)。
* 清除 CPSR 的 BIT J (控制核心是否跑在 Jazelle state),並且根據 SCTLR 的 BIT EE (控制 Exception 是跑在大端序 Big-endian 還是小端序 Little-endian) 來設定 CPSR 的 BIT E (控制讀寫得端序)。
5. 將 PC (Program Counter) 指向異常向量表中該 Exception Type 的相對應位置並開始執行 Exception Handler。
Exception Handler 的實做內容嚴格上來說是由開發者自由設計,但大部分時候都會符合以下的流程:
1. 第一步幾乎都必須是要將當下的暫存器存起來 (透過 Push 將 R0~R12 和 LR 存進 Stack 中或是透過 [SRS, Store Return State](https://developer.arm.com/documentation/dui0379/e/arm-and-thumb-instructions/srs) 將 LR 和 SPSR 存進 Stack 中),以便 Exception Handler 使用他們。
2. 執行 Exception Handler。
3. 將 CPSR、暫存器、PC 恢復原值來跳回出問題時的時間點,可以有以下幾種方法:
* 若不需要將暫存器做還原,只需要單純地將 PC 指回問題發生時間點的話,可以在將 PC 恢復成 LR 的值時,在 SUB 或者 MOV 後面加上 S,例如: SUBS pc, lr, #4。這個 S 在非 User 模式下且目的暫存器為 PC 時,表示同時將 CPSR 恢復成 SPSR 的值 (在 User 模式下則表示計算結果會影響 CPSR 中的 BIT N Z C V)。
* 如果有暫存器需要做還原,那可以透過 LDMFD (Load Multiple Full Descending) 來一口氣從 Stack 中還原 CPSR、暫存器和 PC:
```LDMFD sp! {R0-R12, pc}^```
^ 一樣用來表示同時將 CPSR 恢復成 SPSR 的值。
* [RFE (Return From Exception)](https://developer.arm.com/documentation/dui0379/e/arm-and-thumb-instructions/rfe) 能夠搭配第一步驟的 SRS 來一口氣從 Stack 中恢復 PC 和 CPSR。
整個異常處理的流程如下圖:

# 7. Exception Handler
## 7.1 Abort Handler
當 Data 或是 Instruction Abort 發生時,通常能修復的空間很有限,所以 Handler 能做的就是紀錄錯誤的相關資訊,並將他報出來以供開發人員除錯,然後終止出錯的程式或停住 (Halt) 整個系統。協處理器 CP15 提供以下的暫存器來紀錄出錯時的資訊:
1. [DFAR (Data Fault Address Register)](https://developer.arm.com/documentation/ddi0601/2024-12/AArch32-Registers/DFAR--Data-Fault-Address-Register?lang=en): 用來儲存造成同步 Data Abort 的虛擬記憶體位置。
2. [DFSR (Data Fault Status Register)](https://developer.arm.com/documentation/ddi0601/2024-12/AArch32-Registers/DFSR--Data-Fault-Status-Register?lang=en): 用來儲存上一個 Data Abort 的相關資訊。其中包含是讀或寫指令造成、是同步還是非同步 Data Abort (也就是說 DFAR 有沒有效)、發生在 Cache 還是外部記憶體系統等等。
3. [IFAR (Instruction Fault Address Register)](https://developer.arm.com/documentation/ddi0601/2024-12/AArch32-Registers/IFAR--Instruction-Fault-Address-Register?lang=en): 用來儲存造成同步 Prefetch Abort 的虛擬記憶體位置。
4. [IFSR (Instruction Fault Status Register)](https://developer.arm.com/documentation/ddi0601/2024-12/AArch32-Registers/IFSR--Instruction-Fault-Status-Register?lang=en): 用來儲存上一個 Prefetch Abort 的相關資訊。其中包含是同步還是非同步 Prefetch Abort (也就是說 IFAR 有沒有效)、出錯環節等等。
## 7.2 Undefined Instruction Handler
Undefined instruction 顧名思義就是試圖去執行一個 Arm 核心認不得的指令,或者是執行一個協處理器指令,但沒有任何協處理器認得。以後者來說,Exception Handler 可以試著排除問題後重新執行指令、或者直接使用軟體來實作缺失的協處理器功能。
舉例來說,有的系統軟體上會包含 VFP (Vector Floating Point,用來加速浮點運算的硬體) 協處理器的程式碼,但是其硬體上不一定有 VFP,或者 VFP 沒有辦法處理該指令,需要軟體支援而產生異常,那麼就可以在 Handler 中以軟體去實作該指令,然後返回原流程繼續執行。另一種可能是 VFP 是處於被關閉的狀態,這情況也同樣會產生異常,這樣 Handler 就可以去把 VFP 打開後恢復原流程再次嘗試執行指令。
若 Undefined 異常沒有辦法被恢復,那麼 Handler 也應該要記錄相關的錯誤資訊以供開發者除錯,並且移除錯誤的程式或停止整個系統。
另外 Undefined Instruction 有個特殊的應用是可以用來實作 User breakpoint。這個可以參考原文[第二十四章 -- Debug](https://developer.arm.com/documentation/den0013/d/Debug?lang=en) 來了解更多資訊。
---
上一篇: [Arm Programmer's Guide X - Memory Ordering 學習筆記](https://hackmd.io/@uMqav-XESBCsrtZEx_Jpug/Arm_Programmer_Guide_Memory_Ordering_Studying_Note)
下一篇: [Arm Programmer's Guide XII - Interrupt Handling 學習筆記](https://hackmd.io/@uMqav-XESBCsrtZEx_Jpug/Arm_Programmer_Guide_Interrupt_Handling_Study_note)